Skip to content

Instantly share code, notes, and snippets.

@reedsa
Last active October 12, 2017 17:06
Show Gist options
  • Save reedsa/4d0f567ab00c6e61279690f23e735d52 to your computer and use it in GitHub Desktop.
Save reedsa/4d0f567ab00c6e61279690f23e735d52 to your computer and use it in GitHub Desktop.

Reselect

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.

React and Redux

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.

So why not use a utility function?

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.

React Rendering Continued

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.

Welcome to 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.

Reselect FTW

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()

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
Composing Selectors

createSelector can also take other memoized selectors as input selectors.

// MEMOIZED SELECTOR USING MEMOIZED SELECTOR AS AN INPUT

Usage with React Redux

In React and Redux, selectors can be called directly within a connected component using mapStateToProps.

Props in Selectors

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.

Shared Selectors

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

API

See the Reselect API Docs for further info about helper functions and customizing selectors/memoization functions.

Tips

  • 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.

Other Cool Stuff

re-reselect can assist with sharing selectors across multiple components. It can also improve performance further in certain cases.

References

Most of this content is from the Redux documentation about Computing Derived Data.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment