Selector Pattern: Painless Redux Store Destructuring
Redux is awesome for how it explicitly defines all possible state transformations (actions) and how those transformations mutate the store (reducers) — but it doesn’t prescribe a way for components to query the store.
One way to solve this problem is through use of the Selector Pattern, centralizing state access concerns in one place and reducing code complexity.
TLDR
- Selector Pattern
- Implementation In a React Component
- Derived Selector Implementations
- Performance Optimization: Caching
This post was peer reviewed by Cameron Sampson
Selector Pattern
The Redux store allows an application to centralize global application state (e.g. state shared across two or more components). This is a good thing in that it reduces application complexity. The selector pattern solves the problem of how the application should fetch values from this centralized state.
A common solution to this problem is to access the store through direct references:
The problem with this approach is that it requires your application code to know the exact shape of the store — and if the shape of the store ever changes, you’ll have to update every single bit of application code that reads from it. It is a solution that continues the good practice of centralizing concerns, which minimizes the complexity of code.
The selector pattern is an abstraction that standardizes an application’s store querying logic. It is simple: for any part of the store that an application needs access to, define a function that when given the full store, returns the desired part (or derivation) of the store.
It can be tempting to skip the selector pattern and have components own some basic knowledge of the shape of the store, especially if it is a simple reference:
I would suggest that for all store queries, an application should use the selector pattern, regardless of the complexity of the look up. If the implementation of the store is ever refactored, the amount of references that will require associated updates will be minimized by using the selector pattern.
Implementation In a React Component
For all store queries, the application need only to pass the full store to the appropriate selector:
In mapStateToProps
, we simply pass store
to the correct selector. This component does not know about the implementation of the selector, merely that a particular selector will return the correct part or derivation of store
.
The selectors are imported from the root reducer module:
Including the selectors in the root reducer module makes sense because the they require knowledge of the shape of the root of an application’s store. This is the module where that knowledge lives. Consequently, if the shape of the root of the store changes, it is possible that this is the only file that will require changes.
These selectors share a common interface: they expect the application’s full store
object as the function’s first argument. In order to resolve the desired value(s), these functions are composed of one or more private selector invocations. As the public selector expects the full store as it’s first argument, each private selector expects a subset of the store as it’s first argument.
Each private selector is associated with a particular subset of the full store in the same way that each reducer configuration is associated with a slice of the full store. Consequently, private selectors and reducers configurations can exist elegantly in the same module:
This public/private pattern of selectors allows:
- the configuration of a reducer to change while the public selector implementation is less likely to have to change
- two or more public selectors can share private selectors for DRYer, easier-to-maintain codes
Derived Selector Implementations
As demonstrated earlier, public selector implementations can be as simple as invoking a single private selector. However, more sophisticated implementations are certainly possible.
- selector that is composed of multiple private selectors: A public selector can return the result of more than a single private selector. Imagine an
isLoading
public selector that is implemented as the result of two or moreisLoading
private selectors:
- selector that computes a result from some part(s) of the
store
:
Performance Optimization: Caching
If one finds that a particular public selector lacks real time performance (e.g. searching on a large list, expensive mathematical computation, etc.), caching of the result of a selector invocation may prove beneficial. The Reselect library is a popular implementation of selector memoization (i.e. caching the results of function invocations, keyed by the passed arguments) that also provides an easy syntax for composing public selectors from multiple private selectors.