-->

Reselect - does it ever make sense to create a mem

2019-06-17 01:36发布

问题:

I have a normal selector which is just used to get part of the state:

export const getAllPosts = state => {
  return state.posts;
};

If I use the reselect to wrap the selector:

import { createSelector } from 'reselect';

export const allPosts = createSelector(
  getAllPosts,
  (posts) => posts
);

Does it make any sense such as improving performance ? In my opinion, the wrapper is unnecessary.

回答1:

No, it does not make sense to create a memoized selector just to get part of the state tree.

The reason is that connect will do it’s own shallow equality check for each prop passed in from mapStateToProps. If the prop returned by the selector passes that shallow equality check, along with the other props, then render will not be called unnecessarily. If the selector simply returned a part of the state tree and that part of the state tree had not been modified then a shallow equality check will be sufficient.

However, If the selector is computed from the results of other selectors then using createSelector is a good choice. Firstly, it provides a nice syntax for composing selectors. Secondly, if the computation of combining the selectors is potentially expensive you will get a performance benifit. Thirdly, if the selector was to return a new, but equivelent, object or array then the shallow equality check provided by connect would not be sufficient. In that case the memoization that createSelector provides would ensure that the same object or array instance was returned if the inputs had not changed and then the shallow equality check would be sufficient to avoid costly re-renders.

So for just exposing parts of the state tree createSelector doesn’t add anything.

For nearly all selectors that are computed from multiple parts of the state tree createSelector begins to add value. The amount of value that it adds varies based on the selector from just being easier to read up to ensuring you don’t re render the component tree unnecessarily.



回答2:

No, that provides no real benefit.

What I've found is that my first-level selectors are simply plain functions, and any selectors that go deeper than that need to be memoized:

// assume state is:  {first : {second {} } }

const selectFirst = state => state.first;

const selectSecond = createSelector(
    selectFirst,
    first => first.second
);


回答3:

In your case it does not make sense cause you just return the same reference from the store which is always shallow equal to itself (or it's previous state) unless you modify it.

If you imagine a different scenario for example a scenario where you store your entities in an object instead of an array in the store but you want to return an array to your Component then you need to derive data:

export const selectAllPosts = createSelector(
  getAllPostsFromStore, // returns { 1: {...}, 2: {...} }
  (allPosts) => Object.keys(allPosts).map(key => allPosts[key])
);

Now your selector turned into a performance boost cause it only computes the derived data when something in the store changes.

So my rule of thumb is: If you don't derive data, you don't need to memoize it.