Reselect is a library from the React Community that helps obtain derived data from the Redux store. Reselect helps create memoized, composable selector functions.
In addition to the performance gains for large state trees or calculations, selectors can help reduce duplication across the Redux store and clean up code for deeply nested state properties.
In React, rendering a large number of components can be an expensive operation. With Redux, the input data is recalculated each time a component renders. This is fine in a basic application but performance can degrade as components become more complex.
By all means use functions to interface with your store. Complex data can be tedious to translate into the format your components need. Utility functions are reusable and easily tested. They also provide a great way to make data retrieval more expressive.
Utility functions start to break down when performance is a concern. It is really easy to take a function that calculates data and turn it into a Reselect selector function. Reselect comes with memoization built in, which may help application performance when data in another part of the store changes but the data needed to render another component has not changed.
Take for example a list of a thousand items. You might use pagination to make the user experience a bit better than scrolling through a huge list, but imagine all the items being rendered on a single page.
You might implement a parent component called Items
that renders each item with another component called Item
. When a change occurs state of the parent component, each one of the props are recalculated even though it may not affect the Item
components at all.
To prevent issues such as this you could modify the component state to use Reselect.
Let's dig into the Items
component a bit further.
// ITEMS COMPONENT EXAMPLE
This React component uses mapStateToProps
each time the Items
component receives new state. When the number of items being manipulated by the selector gets large enough it becomes inefficient to call the selector when the underlying data has not changed. Each time the user updates the input it has to call mapStateToProps
and subsequently renders each of the Item
components.
Avoiding recalculations altogether by using memoized selectors is a good way to fix performance problems like this.
A memoized selector is a function that recalculates state only when the value of the particular state it is using changes.
Instead of calling the selector every time, Reselect checks if the input data to the selector has changed and only calls the function if the data has changed. Otherwise, the selector will just return the previously computed value.
createSelector(selectors, transformFunction)
With createSelector, you pass in an array of selector
functions that return parts of your state (or are themselves a memoized selector) along with a transformFunction
that makes the data calculation and returns the result using the state values returned from the selector
functions as inputs.
Whenever the Redux state tree changes, the input selector
values are checked to determine if the transformFunction
should be called.
// MEMOIZED SELECTOR EXAMPLE
createSelector
can also take other memoized selectors as input selectors.
// MEMOIZED SELECTOR USING MEMOIZED SELECTOR AS AN INPUT
In React and Redux, selectors can be called directly within a connected component using mapStateToProps
.
Sometimes it is necessary to use Props passed into a component to select the correct data from your Redux store.
// EXAMPLE WITH SELECTORS USING PROPS
How is this memoized? It's not, but there's a solution.
Memoization is possible between separate components using a selector. In this case, each instance of the component needs its own copy of the selector.
It's easy to wrap the existing selector in a function that returns a new selector.
The key here is with react-redux:
If the mapStateToProps argument supplied to connect returns a function instead of an object, it will be used to create an individual mapStateToProps function for each instance of the container.
With this in mind, we can update mapStateToProps
in our connected component.
// EXAMPLE OF SELECTOR WRAPPED IN A FACTORY FUNCTION
// EXAMPLE COMPONENT USING makeMapStateToProps
See the Reselect API Docs for further info about helper functions and customizing selectors/memoization functions.
- Reselect is most useful for large applications when performance and rendering becomes an issue.
- If you know your application is going to grow in complexity, it's probably a good idea to start using Reselect early on rather than adopting later. It can help establish patterns that can be reused and tested as your team implements new features.
- Inputs to selectors created with
createSelector
should be immutable. - Don't mutate state objects. This is not how you should Redux anyway, but if you mutate state you wont see changes happening in selector inputs.
- Reselect does not limit you to using Redux. It can be used with many apps, including ones that use Flux.
- Testing selectors is dead simple. Never leave a selector untested. See the docs for more information.
re-reselect can assist with sharing selectors across multiple components. It can also improve performance further in certain cases.
Most of this content is from the Redux documentation about Computing Derived Data.