Optimize tags and followedTags
followedTags: - Moved the filtering to the reducer side, so that we don't do it every time. We can't rely on `createSelector` because the store will be invalidated on each `USER_STATE_POPULATE`, unfortunately. tags: - Memoize via re-reselect for the "ForUri" selector.
This commit is contained in:
parent
0c2c21b67e
commit
4b0318cd38
4 changed files with 31 additions and 11 deletions
|
@ -1,10 +1,10 @@
|
|||
import { connect } from 'react-redux';
|
||||
import { makeSelectTagsForUri } from 'redux/selectors/claims';
|
||||
import { selectTagsForUri } from 'redux/selectors/claims';
|
||||
import { selectFollowedTags } from 'redux/selectors/tags';
|
||||
import ClaimTags from './view';
|
||||
|
||||
const select = (state, props) => ({
|
||||
tags: makeSelectTagsForUri(props.uri)(state),
|
||||
tags: selectTagsForUri(state, props.uri),
|
||||
followedTags: selectFollowedTags(state),
|
||||
});
|
||||
|
||||
|
|
|
@ -65,11 +65,22 @@ export default handleActions(
|
|||
[ACTIONS.USER_STATE_POPULATE]: (state: TagState, action: { data: { tags: ?Array<string> } }) => {
|
||||
const { tags } = action.data;
|
||||
if (Array.isArray(tags)) {
|
||||
const next = tags && tags.filter((tag) => typeof tag === 'string');
|
||||
const prev = state.followedTags;
|
||||
|
||||
if (next && prev && prev.length === next.length && prev.every((value, index) => value === next[index])) {
|
||||
// No changes
|
||||
return state;
|
||||
}
|
||||
|
||||
// New state
|
||||
return {
|
||||
...state,
|
||||
followedTags: tags,
|
||||
followedTags: next || [],
|
||||
};
|
||||
}
|
||||
|
||||
// Purge 'followedTags'
|
||||
return {
|
||||
...state,
|
||||
};
|
||||
|
|
|
@ -270,6 +270,11 @@ export const makeSelectTotalPagesInChannelSearch = (uri: string) =>
|
|||
return byChannel['pageCount'];
|
||||
});
|
||||
|
||||
export const selectMetadataForUri = createCachedSelector(selectClaimForUri, (claim, uri) => {
|
||||
const metadata = claim && claim.value;
|
||||
return metadata || (claim === undefined ? undefined : null);
|
||||
})((state, uri) => uri);
|
||||
|
||||
export const makeSelectMetadataForUri = (uri: string) =>
|
||||
createSelector(makeSelectClaimForUri(uri), (claim) => {
|
||||
const metadata = claim && claim.value;
|
||||
|
@ -537,6 +542,10 @@ export const makeSelectMyChannelPermUrlForName = (name: string) =>
|
|||
return matchingClaim ? matchingClaim.permanent_url : null;
|
||||
});
|
||||
|
||||
export const selectTagsForUri = createCachedSelector(selectMetadataForUri, (metadata: ?GenericMetadata) => {
|
||||
return (metadata && metadata.tags) || [];
|
||||
})((state, uri) => uri);
|
||||
|
||||
export const makeSelectTagsForUri = (uri: string) =>
|
||||
createSelector(makeSelectMetadataForUri(uri), (metadata: ?GenericMetadata) => {
|
||||
return (metadata && metadata.tags) || [];
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
// @flow
|
||||
import { createSelector } from 'reselect';
|
||||
|
||||
const selectState = (state: { tags: TagState }) => state.tags || {};
|
||||
type State = { tags: TagState };
|
||||
|
||||
const selectState = (state: State) => state.tags || {};
|
||||
|
||||
export const selectKnownTagsByName = createSelector(selectState, (state: TagState): KnownTags => state.knownTags);
|
||||
|
||||
export const selectFollowedTagsList = createSelector(selectState, (state: TagState): Array<string> =>
|
||||
state.followedTags.filter(tag => typeof tag === 'string')
|
||||
);
|
||||
export const selectFollowedTagsList = (state: State) => selectState(state).followedTags;
|
||||
|
||||
export const selectFollowedTags = createSelector(selectFollowedTagsList, (followedTags: Array<string>): Array<Tag> =>
|
||||
followedTags.map(tag => ({ name: tag.toLowerCase() })).sort((a, b) => a.name.localeCompare(b.name))
|
||||
followedTags.map((tag) => ({ name: tag.toLowerCase() })).sort((a, b) => a.name.localeCompare(b.name))
|
||||
);
|
||||
|
||||
export const selectUnfollowedTags = createSelector(
|
||||
|
@ -19,7 +19,7 @@ export const selectUnfollowedTags = createSelector(
|
|||
(tagsByName: KnownTags, followedTags: Array<string>): Array<Tag> => {
|
||||
const followedTagsSet = new Set(followedTags);
|
||||
let tagsToReturn = [];
|
||||
Object.keys(tagsByName).forEach(key => {
|
||||
Object.keys(tagsByName).forEach((key) => {
|
||||
if (!followedTagsSet.has(key)) {
|
||||
const { name } = tagsByName[key];
|
||||
tagsToReturn.push({ name: name.toLowerCase() });
|
||||
|
@ -31,6 +31,6 @@ export const selectUnfollowedTags = createSelector(
|
|||
);
|
||||
|
||||
export const makeSelectIsFollowingTag = (tag: string) =>
|
||||
createSelector(selectFollowedTags, followedTags => {
|
||||
return followedTags.some(followedTag => followedTag.name === tag.toLowerCase());
|
||||
createSelector(selectFollowedTags, (followedTags) => {
|
||||
return followedTags.some((followedTag) => followedTag.name === tag.toLowerCase());
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue