Don't update 'byId' if no changes + add 'selectClaimWithId'
## Ticket 116 Claim store optimization ideas (reducing unnecessary renders) ## Changes - Ignore things like `confirmations` so that already-fetched claims aren't invalidated and causes re-rendering. The `stringify` might look expensive, but the amount of avoided re-renders outweighs it. There might be faster ways to compare, though. - With `byId[claimId]` references more stable now, memoized selectors can now use 'selectClaimWithId' to pick a specific claim to depend on, instead of 'byId' which changes on every update.
This commit is contained in:
parent
de2bec4425
commit
0736723200
2 changed files with 56 additions and 8 deletions
|
@ -130,6 +130,35 @@ function resolveDelta(original: any, delta: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function claimObjHasNewData(original, fresh) {
|
||||||
|
// Don't blow away 'is_my_output' just because the next query didn't ask for it.
|
||||||
|
const ignoreIsMyOutput = original.is_my_output !== undefined && fresh.is_my_output === undefined;
|
||||||
|
|
||||||
|
// Something is causing the tags to be re-ordered differently
|
||||||
|
// (https://github.com/OdyseeTeam/odysee-frontend/issues/116#issuecomment-962747147).
|
||||||
|
// Just do a length comparison for now, which covers 99% of cases while we
|
||||||
|
// figure out what's causing the order to change.
|
||||||
|
const ignoreTags =
|
||||||
|
original.value &&
|
||||||
|
fresh.value &&
|
||||||
|
original.value.tags &&
|
||||||
|
fresh.value.tags &&
|
||||||
|
original.value.tags.length !== fresh.value.tags.length;
|
||||||
|
|
||||||
|
const excludeKeys = (key, value) => {
|
||||||
|
if (key === 'confirmations' || (ignoreTags && key === 'tags') || (ignoreIsMyOutput && key === 'is_my_output')) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
const originalStringified = JSON.stringify(original, excludeKeys);
|
||||||
|
const freshStringified = JSON.stringify(fresh, excludeKeys);
|
||||||
|
|
||||||
|
return originalStringified !== freshStringified;
|
||||||
|
}
|
||||||
|
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
// handleClaimAction
|
// handleClaimAction
|
||||||
// ****************************************************************************
|
// ****************************************************************************
|
||||||
|
@ -138,7 +167,7 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
const { resolveInfo }: ClaimActionResolveInfo = action.data;
|
||||||
|
|
||||||
const byUri = Object.assign({}, state.claimsByUri);
|
const byUri = Object.assign({}, state.claimsByUri);
|
||||||
const byId = Object.assign({}, state.byId);
|
const byIdDelta = {};
|
||||||
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
const channelClaimCounts = Object.assign({}, state.channelClaimCounts);
|
||||||
const pendingById = state.pendingById;
|
const pendingById = state.pendingById;
|
||||||
let newResolvingUrls = new Set(state.resolvingUris);
|
let newResolvingUrls = new Set(state.resolvingUris);
|
||||||
|
@ -151,10 +180,13 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
|
|
||||||
if (stream) {
|
if (stream) {
|
||||||
if (pendingById[stream.claim_id]) {
|
if (pendingById[stream.claim_id]) {
|
||||||
byId[stream.claim_id] = mergeClaim(stream, byId[stream.claim_id]);
|
byIdDelta[stream.claim_id] = mergeClaim(stream, state.byId[stream.claim_id]);
|
||||||
} else {
|
} else {
|
||||||
byId[stream.claim_id] = stream;
|
if (!state.byId[stream.claim_id] || claimObjHasNewData(state.byId[stream.claim_id], stream)) {
|
||||||
|
byIdDelta[stream.claim_id] = stream;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
byUri[url] = stream.claim_id;
|
byUri[url] = stream.claim_id;
|
||||||
|
|
||||||
// If url isn't a canonical_url, make sure that is added too
|
// If url isn't a canonical_url, make sure that is added too
|
||||||
|
@ -181,9 +213,9 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pendingById[channel.claim_id]) {
|
if (pendingById[channel.claim_id]) {
|
||||||
byId[channel.claim_id] = mergeClaim(channel, byId[channel.claim_id]);
|
byIdDelta[channel.claim_id] = mergeClaim(channel, state.byId[channel.claim_id]);
|
||||||
} else {
|
} else {
|
||||||
byId[channel.claim_id] = channel;
|
byIdDelta[channel.claim_id] = channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
byUri[channel.permanent_url] = channel.claim_id;
|
byUri[channel.permanent_url] = channel.claim_id;
|
||||||
|
@ -194,10 +226,11 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
|
|
||||||
if (collection) {
|
if (collection) {
|
||||||
if (pendingById[collection.claim_id]) {
|
if (pendingById[collection.claim_id]) {
|
||||||
byId[collection.claim_id] = mergeClaim(collection, byId[collection.claim_id]);
|
byIdDelta[collection.claim_id] = mergeClaim(collection, state.byId[collection.claim_id]);
|
||||||
} else {
|
} else {
|
||||||
byId[collection.claim_id] = collection;
|
byIdDelta[collection.claim_id] = collection;
|
||||||
}
|
}
|
||||||
|
|
||||||
byUri[url] = collection.claim_id;
|
byUri[url] = collection.claim_id;
|
||||||
byUri[collection.canonical_url] = collection.claim_id;
|
byUri[collection.canonical_url] = collection.claim_id;
|
||||||
byUri[collection.permanent_url] = collection.claim_id;
|
byUri[collection.permanent_url] = collection.claim_id;
|
||||||
|
@ -216,7 +249,7 @@ function handleClaimAction(state: State, action: any): State {
|
||||||
});
|
});
|
||||||
|
|
||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
byId,
|
byId: resolveDelta(state.byId, byIdDelta),
|
||||||
claimsByUri: byUri,
|
claimsByUri: byUri,
|
||||||
channelClaimCounts,
|
channelClaimCounts,
|
||||||
resolvingUris: Array.from(newResolvingUrls),
|
resolvingUris: Array.from(newResolvingUrls),
|
||||||
|
|
|
@ -43,6 +43,21 @@ export const selectClaimsByUri = createSelector(selectClaimIdsByUri, selectClaim
|
||||||
return claims;
|
return claims;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the claim with the specified ID. The claim could be undefined if does
|
||||||
|
* not exist or have not fetched. Take note of the second parameter, which means
|
||||||
|
* an inline function or helper would be required when used as an input to
|
||||||
|
* 'createSelector'.
|
||||||
|
*
|
||||||
|
* @param state
|
||||||
|
* @param claimId
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export const selectClaimWithId = (state: State, claimId: string) => {
|
||||||
|
const byId = selectClaimsById(state);
|
||||||
|
return byId[claimId];
|
||||||
|
};
|
||||||
|
|
||||||
export const selectAllClaimsByChannel = createSelector(selectState, (state) => state.paginatedClaimsByChannel || {});
|
export const selectAllClaimsByChannel = createSelector(selectState, (state) => state.paginatedClaimsByChannel || {});
|
||||||
|
|
||||||
export const selectPendingIds = createSelector(selectState, (state) => Object.keys(state.pendingById) || []);
|
export const selectPendingIds = createSelector(selectState, (state) => Object.keys(state.pendingById) || []);
|
||||||
|
|
Loading…
Reference in a new issue