Fix reaction-selector reference invalidation
## Issue When comments are refreshed, each `Comment` gets rendered 4-5 times due to reference invalidation for `othersReacts` (the data didn't actually change). ## Change For selectors without transformation, there is no need to memoize using `createSelector` -- just access it directly. Also, don't do things like `return a[id] || {}` in a reducer, because the reference to the empty object will be different on each call. Always return directly from the state so that the same reference is returned. This simple change avoided the wasted resources needed for `createSelector`, and reduced to render to just 2 (initial render, and when reactions are fetched).
This commit is contained in:
parent
249b73f8c6
commit
9bbd72d179
6 changed files with 28 additions and 41 deletions
|
@ -12,7 +12,7 @@ import { doSetPlayingUri } from 'redux/actions/content';
|
||||||
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
import { selectUserVerifiedEmail } from 'redux/selectors/user';
|
||||||
import {
|
import {
|
||||||
selectLinkedCommentAncestors,
|
selectLinkedCommentAncestors,
|
||||||
makeSelectOthersReactionsForComment,
|
selectOthersReactsForComment,
|
||||||
makeSelectTotalReplyPagesForParentId,
|
makeSelectTotalReplyPagesForParentId,
|
||||||
} from 'redux/selectors/comments';
|
} from 'redux/selectors/comments';
|
||||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||||
|
@ -29,7 +29,7 @@ const select = (state, props) => {
|
||||||
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state),
|
thumbnail: props.authorUri && makeSelectThumbnailForUri(props.authorUri)(state),
|
||||||
channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state),
|
channelIsBlocked: props.authorUri && makeSelectChannelIsMuted(props.authorUri)(state),
|
||||||
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
|
commentingEnabled: IS_WEB ? Boolean(selectUserVerifiedEmail(state)) : true,
|
||||||
othersReacts: makeSelectOthersReactionsForComment(reactionKey)(state),
|
othersReacts: selectOthersReactsForComment(state, reactionKey),
|
||||||
activeChannelClaim,
|
activeChannelClaim,
|
||||||
myChannels: selectMyChannelClaims(state),
|
myChannels: selectMyChannelClaims(state),
|
||||||
playingUri: selectPlayingUri(state),
|
playingUri: selectPlayingUri(state),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import Comment from './view';
|
||||||
import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'redux/selectors/claims';
|
import { makeSelectClaimIsMine, makeSelectClaimForUri } from 'redux/selectors/claims';
|
||||||
import { doResolveUri } from 'redux/actions/claims';
|
import { doResolveUri } from 'redux/actions/claims';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import { makeSelectMyReactionsForComment, makeSelectOthersReactionsForComment } from 'redux/selectors/comments';
|
import { selectMyReactsForComment, selectOthersReactsForComment } from 'redux/selectors/comments';
|
||||||
import { doCommentReact } from 'redux/actions/comments';
|
import { doCommentReact } from 'redux/actions/comments';
|
||||||
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
import { selectActiveChannelClaim } from 'redux/selectors/app';
|
||||||
|
|
||||||
|
@ -15,8 +15,8 @@ const select = (state, props) => {
|
||||||
return {
|
return {
|
||||||
claim: makeSelectClaimForUri(props.uri)(state),
|
claim: makeSelectClaimForUri(props.uri)(state),
|
||||||
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
claimIsMine: makeSelectClaimIsMine(props.uri)(state),
|
||||||
myReacts: makeSelectMyReactionsForComment(reactionKey)(state),
|
myReacts: selectMyReactsForComment(state, reactionKey),
|
||||||
othersReacts: makeSelectOthersReactionsForComment(reactionKey)(state),
|
othersReacts: selectOthersReactsForComment(state, reactionKey),
|
||||||
activeChannelId,
|
activeChannelId,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -73,12 +73,12 @@ export default function CommentReactions(props: Props) {
|
||||||
const shouldHide = !canCreatorReact && hideCreatorLike;
|
const shouldHide = !canCreatorReact && hideCreatorLike;
|
||||||
const creatorLiked = getCountForReact(REACTION_TYPES.CREATOR_LIKE) > 0;
|
const creatorLiked = getCountForReact(REACTION_TYPES.CREATOR_LIKE) > 0;
|
||||||
const likeIcon = SIMPLE_SITE
|
const likeIcon = SIMPLE_SITE
|
||||||
? myReacts.includes(REACTION_TYPES.LIKE)
|
? myReacts && myReacts.includes(REACTION_TYPES.LIKE)
|
||||||
? ICONS.FIRE_ACTIVE
|
? ICONS.FIRE_ACTIVE
|
||||||
: ICONS.FIRE
|
: ICONS.FIRE
|
||||||
: ICONS.UPVOTE;
|
: ICONS.UPVOTE;
|
||||||
const dislikeIcon = SIMPLE_SITE
|
const dislikeIcon = SIMPLE_SITE
|
||||||
? myReacts.includes(REACTION_TYPES.DISLIKE)
|
? myReacts && myReacts.includes(REACTION_TYPES.DISLIKE)
|
||||||
? ICONS.SLIME_ACTIVE
|
? ICONS.SLIME_ACTIVE
|
||||||
: ICONS.SLIME
|
: ICONS.SLIME
|
||||||
: ICONS.DOWNVOTE;
|
: ICONS.DOWNVOTE;
|
||||||
|
|
|
@ -13,8 +13,8 @@ import {
|
||||||
selectIsFetchingCommentsById,
|
selectIsFetchingCommentsById,
|
||||||
selectIsFetchingReacts,
|
selectIsFetchingReacts,
|
||||||
makeSelectTotalCommentsCountForUri,
|
makeSelectTotalCommentsCountForUri,
|
||||||
selectOthersReactsById,
|
selectOthersReacts,
|
||||||
selectMyReactionsByCommentId,
|
selectMyReacts,
|
||||||
makeSelectCommentIdsForUri,
|
makeSelectCommentIdsForUri,
|
||||||
selectSettingsByChannelId,
|
selectSettingsByChannelId,
|
||||||
makeSelectPinnedCommentsForUri,
|
makeSelectPinnedCommentsForUri,
|
||||||
|
@ -47,8 +47,8 @@ const select = (state, props) => {
|
||||||
isFetchingReacts: selectIsFetchingReacts(state),
|
isFetchingReacts: selectIsFetchingReacts(state),
|
||||||
fetchingChannels: selectFetchingMyChannels(state),
|
fetchingChannels: selectFetchingMyChannels(state),
|
||||||
settingsByChannelId: selectSettingsByChannelId(state),
|
settingsByChannelId: selectSettingsByChannelId(state),
|
||||||
myReactsByCommentId: selectMyReactionsByCommentId(state),
|
myReactsByCommentId: selectMyReacts(state),
|
||||||
othersReactsById: selectOthersReactsById(state),
|
othersReactsById: selectOthersReacts(state),
|
||||||
activeChannelId: activeChannelClaim && activeChannelClaim.claim_id,
|
activeChannelId: activeChannelClaim && activeChannelClaim.claim_id,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,8 +9,8 @@ import { selectClaimsByUri, selectMyChannelClaims } from 'redux/selectors/claims
|
||||||
import { doClaimSearch } from 'redux/actions/claims';
|
import { doClaimSearch } from 'redux/actions/claims';
|
||||||
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
import { doToast, doSeeNotifications } from 'redux/actions/notifications';
|
||||||
import {
|
import {
|
||||||
makeSelectMyReactionsForComment,
|
selectMyReactsForComment,
|
||||||
makeSelectOthersReactionsForComment,
|
selectOthersReactsForComment,
|
||||||
selectPendingCommentReacts,
|
selectPendingCommentReacts,
|
||||||
selectModerationBlockList,
|
selectModerationBlockList,
|
||||||
selectModerationDelegatorsById,
|
selectModerationDelegatorsById,
|
||||||
|
@ -466,8 +466,8 @@ export function doCommentReact(commentId: string, type: string) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const reactKey = `${commentId}:${activeChannelClaim.claim_id}`;
|
const reactKey = `${commentId}:${activeChannelClaim.claim_id}`;
|
||||||
const myReacts = makeSelectMyReactionsForComment(reactKey)(state);
|
const myReacts = selectMyReactsForComment(state, reactKey) || [];
|
||||||
const othersReacts = makeSelectOthersReactionsForComment(reactKey)(state);
|
const othersReacts = selectOthersReactsForComment(state, reactKey) || {};
|
||||||
|
|
||||||
const signatureData = await channelSignName(activeChannelClaim.claim_id, activeChannelClaim.name);
|
const signatureData = await channelSignName(activeChannelClaim.claim_id, activeChannelClaim.name);
|
||||||
if (!signatureData) {
|
if (!signatureData) {
|
||||||
|
|
|
@ -6,6 +6,8 @@ import { selectBlacklistedOutpointMap, selectFilteredOutpointMap } from 'lbryinc
|
||||||
import { selectClaimsById, selectMyActiveClaims } from 'redux/selectors/claims';
|
import { selectClaimsById, selectMyActiveClaims } from 'redux/selectors/claims';
|
||||||
import { isClaimNsfw } from 'util/claim';
|
import { isClaimNsfw } from 'util/claim';
|
||||||
|
|
||||||
|
type State = { comments: CommentsState };
|
||||||
|
|
||||||
const selectState = (state) => state.comments || {};
|
const selectState = (state) => state.comments || {};
|
||||||
|
|
||||||
export const selectCommentsById = createSelector(selectState, (state) => state.commentById || {});
|
export const selectCommentsById = createSelector(selectState, (state) => state.commentById || {});
|
||||||
|
@ -13,7 +15,17 @@ export const selectIsFetchingComments = createSelector(selectState, (state) => s
|
||||||
export const selectIsFetchingCommentsById = createSelector(selectState, (state) => state.isLoadingById);
|
export const selectIsFetchingCommentsById = createSelector(selectState, (state) => state.isLoadingById);
|
||||||
export const selectIsFetchingCommentsByParentId = createSelector(selectState, (state) => state.isLoadingByParentId);
|
export const selectIsFetchingCommentsByParentId = createSelector(selectState, (state) => state.isLoadingByParentId);
|
||||||
export const selectIsFetchingReacts = createSelector(selectState, (state) => state.isFetchingReacts);
|
export const selectIsFetchingReacts = createSelector(selectState, (state) => state.isFetchingReacts);
|
||||||
export const selectOthersReactsById = createSelector(selectState, (state) => state.othersReactsByCommentId);
|
|
||||||
|
export const selectMyReacts = (state: State) => state.comments.myReactsByCommentId;
|
||||||
|
export const selectMyReactsForComment = (state: State, commentIdChannelId: string) => {
|
||||||
|
// @commentIdChannelId: Format = 'commentId:MyChannelId'
|
||||||
|
return state.comments.myReactsByCommentId && state.comments.myReactsByCommentId[commentIdChannelId];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const selectOthersReacts = (state: State) => state.comments.othersReactsByCommentId;
|
||||||
|
export const selectOthersReactsForComment = (state: State, id: string) => {
|
||||||
|
return state.comments.othersReactsByCommentId && state.comments.othersReactsByCommentId[id];
|
||||||
|
};
|
||||||
|
|
||||||
export const selectPinnedCommentsById = createSelector(selectState, (state) => state.pinnedCommentsById);
|
export const selectPinnedCommentsById = createSelector(selectState, (state) => state.pinnedCommentsById);
|
||||||
export const makeSelectPinnedCommentsForUri = (uri: string) =>
|
export const makeSelectPinnedCommentsForUri = (uri: string) =>
|
||||||
|
@ -177,31 +189,6 @@ export const makeSelectCommentIdsForUri = (uri: string) =>
|
||||||
return state.byId[claimId];
|
return state.byId[claimId];
|
||||||
});
|
});
|
||||||
|
|
||||||
export const selectMyReactionsByCommentId = createSelector(selectState, (state) => state.myReactsByCommentId);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* makeSelectMyReactionsForComment
|
|
||||||
*
|
|
||||||
* @param commentIdChannelId Format = "commentId:MyChannelId".
|
|
||||||
*/
|
|
||||||
export const makeSelectMyReactionsForComment = (commentIdChannelId: string) =>
|
|
||||||
createSelector(selectState, (state) => {
|
|
||||||
if (!state.myReactsByCommentId) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.myReactsByCommentId[commentIdChannelId] || [];
|
|
||||||
});
|
|
||||||
|
|
||||||
export const makeSelectOthersReactionsForComment = (commentId: string) =>
|
|
||||||
createSelector(selectState, (state) => {
|
|
||||||
if (!state.othersReactsByCommentId) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
return state.othersReactsByCommentId[commentId] || {};
|
|
||||||
});
|
|
||||||
|
|
||||||
export const selectPendingCommentReacts = createSelector(selectState, (state) => state.pendingCommentReactions);
|
export const selectPendingCommentReacts = createSelector(selectState, (state) => state.pendingCommentReactions);
|
||||||
|
|
||||||
export const selectSettingsByChannelId = createSelector(selectState, (state) => state.settingsByChannelId);
|
export const selectSettingsByChannelId = createSelector(selectState, (state) => state.settingsByChannelId);
|
||||||
|
|
Loading…
Reference in a new issue