This module exports a single function which creates a memoized state selector for use with the Redux global application state.
createSelector
accepts the following arguments:
- A function which selects data from the state. Given a state object and any number of other arguments it calculates a result, which is cached for later reuse.
- A function which returns the parts of the state tree the selector depends on. This can be a function or array of functions which returns an array of state values.
- (Optional) A function to customize the cache key used by the inner memoized function
For example, our state contains post objects, each of which is assigned to a particular site. To retrieve an array of posts for a specific site, we'd have to filter all of the known posts. This is an expensive operation. Using createSelector
, we can create a memoized function which saves us from having to make that expensive computation over and over:
export const getSitePosts = createSelector(
( state, siteId ) => state.posts.filter( ( post ) => post.site_ID === siteId ),
( state ) => [ state.posts ]
);
To use the selector created with createSelector
, we only need to consider the signature of the function we passed as the first argument. In this case, we'll need to pass a state object and site ID.
const sitePosts = getSitePosts( state, siteId );
This result would only be calculated once, so long as state.posts
remains the same.
We strive to keep redundant data out of our Redux state tree, but this can come at the cost of performance if our selectors are time-consuming in how they evaluate and filter the minimal state.
A memoized selector can alleviate this concern by storing a cached result, skipping these expensive derivations when we know that the result has already been computed once before from the same state.
Because Redux discourages us from mutating state directly, we can be confident that state is only considered to be changed if the segments of the state tree we're concerned with are strictly unequal to a previous state.
When creating a memoized selector via createSelector
, we pass a function which returns a value or array of values comprising of the parts of the tree we depend upon for our selector.
Yes! This is a very common pattern in our state selectors, and is a key differentiator from reselect
, another popular tool which achieves a similar goal.
Do note that the internal memoized function calculates its cache key by a simple Array.prototype.join
call, so your arguments should not be complex objects.
Let's assume your new selector depends on the state selectors foo
, bar
, and baz
. You could then state that like so:
createSelector(
( state ) => foo( state ) && bar( state ),
( state ) => [ foo( state ), bar( state ), baz( state ) ]
);
Since this is a recurring pattern, there is a shorthand for this situation. You can reduce the above to an array just the selectors:
createSelector( ( state ) => foo( state ) && bar( state ), [ foo, bar, baz ] );
While you should rarely need to do so, you can manage the internal Lodash memoize.Cache
instance on the memoizedSelector
property of the returned function.