Prevent Unnecessary Rerenders of Compound Components using React Context

Kent C. Dodds
InstructorKent C. Dodds
Share this video with your friends

Social Share Links

Send Tweet
Published 6 years ago
Updated 3 years ago

Due to the way that React Context Providers work, our current implementation re-renders all our compound component consumers with every render of the <Toggle /> which could lead to unnecessary re-renders. Let's fix that by ensuring that the value prop we pass to the <ToggleContext.Provider /> is only changed when the state changes.

Instructor: [00:00] One more thing we need to consider in our use of context with our toggle compound components is our use of the value prop for our provider. With the way that the provider works, any time the value prop changes, it's going to re-render all consumers, so our compound components.

[00:17] Because the way that we're setting this value, the value prop actually changes every single render. We can visualize that here, if we extract this to a value object. We can see clearly that this value object is getting created every single time this render function is run.

[00:32] In our simple use case, the only time this render method will be run is when the on state changes, because we have no other mechanism for re-rendering the application. In a real application, the toggle component could be re-rendered anytime this usage component is re-rendered.

[00:47] Whether or not the on state has changed, our toggle provider is going to re-render all of the consumers because the value object is brand new on every render. This could lead to a performance bottleneck, and so it'd be nice to avoid this.

[01:01] What we really want is the value to only be updated when the state is updated. Something we could do is add the toggle function to our state, and then we'll have to move that below the definition of the toggle function.

[01:12] Then, instead of this value object, we could provide all of state. This has the effect of providing the exact same object on every render except when the state is being updated, which is exactly what we want.

[01:24] It might feel a little awkward to provide an event handler in state, and normally, you wouldn't want to do this. In the case of context, this is a good trade-off to make in favor of avoiding unnecessary re-renders. With that, our toggle button is still working.

Matt Greer
Matt Greer
~ 6 years ago

Is this correct? I don't believe context providers check to see if the value prop is a new reference. I hacked at the 03.extra-2 exercise in the codebox and despite Toggle.Provider's value object not changing, all the children still render: https://codesandbox.io/s/0o1vr39yow (check the console)

Kent C. Dodds
Kent C. Doddsinstructor
~ 6 years ago

Hey Matt, Honestly, it's a pretty edge case and perhaps I shouldn't have included it in the video. The only time this will make a difference is if the consumers are below a PureComponent or shouldComponentUpdate that returns false: https://codesandbox.io/s/50y9y2z0x4?module=%2Fsrc%2Fexercises-final%2F03.extra-2.js

Pretty edge case-y. I probably shouldn't even bother teaching it. But now you know!

Christian
Christian
~ 5 years ago

Hi Kent, not so much edgy, I guess, considering that all the Redux connected components are 'pure' by default. So thanks for showing this. :)

Babs Craig
Babs Craig
~ 5 years ago

Hi Kent, what happens in a scenario where one of the values being passed to ToggleContext.Provider is being set by props? So for instance if we were passing <Ticker someProp={withSomeData} /> and we want to pass someProp down through the Provider inside of the Ticker component? What would be the best way to accomplish this? Thanks!

Kent C. Dodds
Kent C. Doddsinstructor
~ 5 years ago

You could just forward it along. The passer of that prop would be responsible for this optimization. This is a bit of an edge case though, so you're probably find to just go with whatever feels easiest, then come back and optimize it if it feels slow.

Markdown supported.
Become a member to join the discussionEnroll Today