A good compromise on Redux’s mapStateToProps memoization

Grégory LE GRAND
May 21 · 5 min read

How to add a simple layer of memoization to Redux connected components and improve application’s performance.

EDIT : There is a better way, jump to the end if you are in a hurry about your app performance.

Image for post
Image for postImage for post

Redux is to me the greatest React companion, easy to use, quite fast to understand and it comes with a very rich ecosystem. The issue many developers face when building a real application is a performance reduction as the project grows, using more and more connected components and dispatching many actions.
The trap comes from the fact every connected component’s mapStateToProps function is called after each dispatched action and reducers computations, because Redux does not know anything about parts of the store your components are interested in.

The official Redux documentation advices the use of Reselect library, but it is very opinionated about the structure of your state selectors and the move may seem unreachable for an existing application with tons of selectors and connected components.

At this step do we need to rewrite the entire app or try to limit the number of actions and connected components?

The pattern I will present you here can be applied to an existing project with only minor modifications of your components and reducers. Although it would not apply to a few specific or exotic components, it avoids most of the useless computations, giving a dramatic breath of air to your application.


A typical structure of a Redux store is often quite well divided at the first level with independent reducers in charge of their own part of the state:

Most actions impact - if not only one part - a very few number of that state parts. And it is the same for connected components, they are often only interested in a few parts of the state.

So what if we could just memoize mapStateToProps by allowing each component to declare for which first level state parts modifications they want to have their mapStateToProps processed?

The hardest part of memoization, when you are not dealing with pure functions, is to find a way to determine if you need or not to process the computation, but a way that has a lower cost than the computation itself 😅.

The easiest solution comes from the observation that it is very easy to specify at the component level which first level parts of the state it is consuming, and it will be the only modification you will have to do, keeping the same mapStateToProps function.

Let’s start from a standard reducer:

We will modify the way it works to version its content:

Instead of returning the modified state in each case, we just feed a newState variable, break, and if it is not null after the switch we increment the version and return it. If not, we just return the original state and the version value remains the same.

Then we have an easy way to tell which part of the state has changed. So let’s write a new selector function that returns a simple combined signature of one or more parts of the state:

The function takes the state as a parameter like any state selector. The second parameter can either be a string or an array of string. It only considers the first level parts of the state, but we could imagine a more complex system that would handle versioning at other levels too, depending on your state structure and needs.

The versioning and signature system could also be designed by keeping references to desired parts of the state, but having references to many old versions of state parts could be very memory consuming, preventing garbage collection to operate.


A typical connected component has a mapStateToProps function, passed as a parameter to the wrapping connect function:

We want our optimization to be as simple as possible to implement. So the only modifications we will do in our component if it is interested in parts 1 & 3 of the state will be:

It basically means that the mapStateToProps function should be processed only if state.parts1 or state.parts3 are modified, otherwise it should use the previously cached result of the mapStateToProps function.

If the mapStateToProps parameter passed to the connect function returns a function instead of an object, the function will be used to generate the mapStateToProps function of each instance of the component. This is a very important point to understand as we need each instance of the component to cache its own results of the mapStateToProps function.

So basically, our memoization function base will look like this:

It does nothing usefull at this step but returning a dedicated instance of the mapStateToProps function to each instance of the component.

Now we can add our layer of memoization:

How does it work ?

  • cachedSignature holds the signature of the state parts we want to react to their changes
  • cachedResult holds the result of the mapStateToProps function
  • memoizedMapStateToProps compares the current signature of state parts computed in getStateSignature with the last one stored in 0cachedSignature, updates cachedResult if needed, then always returns cachedResult .

That’s all ! It is so light that I have not even tried to have you install it as a new library :-)

Tips :

  • Start to implement on the persistent connected components in your application to maximize the performance profits. (Header, footer, menus, etc.)
  • Do not forget to update the state parts list when you update your component’s mapStateToProps

Do not hesitate to give feedback about improvements or performance gains you have achieved with the pattern !


EDIT / The better way

Writing about the way you do things has the benefit to make you read the docs again, evaluate the pertinence of your work and do some research.

The redux’s connect function has in fact a few other parameters among which areStatesEqual which allows to precise if the state has changed or not. It is well explained by Julien De Luca in this story :

So I have realized that adding a versioning on state parts was useless when you have the opportunity to access previous state to do the arbitration. Memoization itself of the mapStateToProps function is no longer necessary.

Here is a connect wrapper I now use to facilitate its usage:

A the component level, it is quite straightforward, and the performance is even better as parts of the state can be pointed very precisely :

The third parameter can be:

  • a string if you are interested in one state path
  • an array of string if the component is binded to multiple parts
  • each string can be a root path (ex: ‘part1’) or a dot separated deepest path (ex: ‘part1.subpart2.thing2)

Enjoy!

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store