Skip to content

Instantly share code, notes, and snippets.

@OliverJAsh
Last active January 27, 2020 14:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save OliverJAsh/dc4bb7ae83479a833792dd3c6856ae78 to your computer and use it in GitHub Desktop.
Save OliverJAsh/dc4bb7ae83479a833792dd3c6856ae78 to your computer and use it in GitHub Desktop.

React Redux: performance considerations when dispatching multiple actions

In a React Redux app, what happens when you dispatch multiple actions in a row?

When used out of the box without any performance optimisations, there are two primary concerns:

  • React will re-render multiple times
  • React Redux will re-evaluate selectors multiple times

There are well known and documented solutions to the "multiple React re-renders" problem, but the other problem is not so well known. In this article I'll demonstrate both problems along with their respective solutions.

React re-renders multiple times

By default, React will re-render once for each dispatched action, including for actions that are dispatched together in the same tick of the event loop.

As an example, here is a React app that fetches some data when a button is clicked, and then dispatches 2 actions:

https://gist.github.com/65bf16f6b388a4c16e20551a8839bdaa

When we profile this, we can see that React is rendering twice (once for each dispatched action):

TODO img

Fortunately there is a well established solution to this: the batch function.

… starting in React-Redux v7 a new batch public API is available to help minimize the number of React re-renders when dispatching actions outside of React event handlers. It wraps React's unstable_batchedUpdate() API, allows any React updates in an event loop tick to be batched together into a single render pass. React already uses this internally for its own event handler callbacks. This API is actually part of the renderer packages like ReactDOM and React Native, not the React core itself.

https://gist.github.com/3e4a678d7131505899997bc71ee8ccf0

When we profile this again, we can see that React is now rendering only once:

TODO img

React Redux re-evaluates selectors multiple times

Each time an action is dispatched, every connected component will be notified, and as a result all of the selectors used by those connected components must be re-evaluated. To say that another way: each time an action is dispatched, every selector in the app must be re-evaluated.

When we're dispatching multiple actions in a row, this can get quite expensive. We only want to re-evaluate those selectors once, rather than once for each disaptched action.

To demonstrate this, let's add an expensive selector to our app. (To simulate an expensive selector I'm just blocking the main thread for 3 seconds.)

https://gist.github.com/1e56f24e1b5dd94feab0e80f0ec38401

When we profile this we can see that React Redux is re-evaluating our expensiveSelector twice (once for each dispatched action):

TODO img

Why does this happen? Redux notifies subscribers after each successfully dispatched action, and amongst those subscribers will be every connected component in the app.

To optimise this, we can utilise redux-batched-subscribe, which is a store enhancer that lets you debounce subscriber calls for multiple dispatches.

https://gist.github.com/bc3bd3621d3a90abbd9f7ee8b42dedef

When we profile this again, we can see that React Redux is now re-evaluating our expensiveSelector only once:

TODO img

Note that I used a single expensive selector here for demonstration purposes, but this optimisation may still be worthwhile even if your selectors are not that expensive—React Redux apps tend to use a lot of selectors, so it's worth considering the aggregate cost of all your selectors when they are all ran at the same time.

Notes

All of the code for this article can be found at on GitHub and StackBlitz.

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