Black Sheep Code

No, react context is not causing too many renders

Published:

I commonly see people having the belief that react context is not appropriate for managing state, because every time its state changes, it'll cause everything under the React provider to re-render.

This is causes people to avoid using context, and jumping straight to tools like Redux or Zustand.

It is a misnomer, and I'm here to disprove it.

Here's my application:

Top level of the application

I do have a button at the top of the application that will re-render the whole application.

This is to demonstrate that there is no trickery here.

Context provider

My context provider is simple. We store state in a useState hook, and provide it via the context provider.

Context consumers

I have two components, both of them use this context.

Unrelated component

I have several instances of an unrelated component that doesn't use the context.

Render tracking component

My render tracking component displays the dot in a different spot each time it renders.

What's the result?

You can see for yourself:

☝️ Interactive demo

State Changer
Render Tracker
State Displayer
foo
Render Tracker
Some unrelated component
Render Tracker
Some unrelated component
Render Tracker
Some unrelated component
Render Tracker

Observe that clicking 'render all' button will in fact cause a render of the entire application.

Observe that clicking the 'Change state' button, only affect the components consuming the context.

Where does this confusion come from?

I think this confusion comes from two things.

1. You really shouldn't bung all your state into one provider.

Were I to add color/setColor, foo/setFoo and bar/setBar pairs to the same context provider, and had a new component FooComponent, using those new parts of state, these state changes will cause re-renders. All the consumers of the one context provider will re-render when state changes.

☝️ Interactive demo

Code
State Changer
Render Tracker
State Displayer
foo
Render Tracker
Foo Component
Render Tracker
Some unrelated component
Render Tracker
Some unrelated component
Render Tracker
Some unrelated component
Render Tracker

Observe that randomizing the color causes renders of the other context consumers.

This is fine if it's all related data and they needed to show the change anyway.

But if you have two sets of unrelated data, then you can just use two context providers!

{children} don't cause renders

I think a lot of the confusion comes from knowing that the render of a component will cause all of its descendants to render.

And because context providers usually live at the top of the application, people believe that the context provider, when it re-renders, will cause everything below it to render.

Unfortunately, the terminology is a bit confusing here!

Consider two seemingly similar components

The first directly renders the RenderTracker.

The second has it passed in via the children prop.

The terminology is a bit ambiguous, in both cases these can be called 'children' in common parlance.

However, they behave a lot differently!

☝️ Interactive demo

Render Tracker
Render Tracker

Observe that the RenderTracker that is passed in as a child does not re-render when the state changes.

Conclusions

React context is not the performance boogeyman that it often made out to be.

This common misconception has people reaching for tools like Redux and Zustand when it's really not needed.

Yes, if you load up dozens of bits of state into one context provider, then you're going to have problems.

But to just pass state between components that are in different parts of your application, it's absolutely fine, and dare I say - it's much tidier solution using a global state provider like Redux of Zustand.

If you really want a performance boogeyman, it's controlled components.

For example here - we can see every keystroke causes a render:

☝️ Interactive demo

Code
Render Tracker

Type in the text box and note a render on every keystroke.

Don't be afraid of context providers. It's often the perfect tool for the job.



Questions? Comments? Criticisms? Get in the comments! 👇

Spotted an error? Edit this page with Github