React Context and Re-Renders: React Take the Wheel
A React context Provider will cause its consumers to re-render whenever the value provided changes.
// first render
<Provider value={1}>// next render
<Provider value={2}>// all consumers will re-render
<Consumer>{value => (/*...*/)}</Consumer>
This no big deal if you’re passing primitive values to value
. If you pass 2
multiple times in a row, the consumers won’t re-render.
However, if you’re passing objects then you’ve got to be more careful. The following code will cause consumers to re-render every time the Menu renders, even if nothing in the Menu actually changed (perhaps the component that rendered Menu is changing state).
let MenuContext = React.createContext()class Menu extends React.Component {
constructor() {
super()
this.state = {
value: this.props.defaultValue
}
} setValue(newValue) {
this.setState({ value: newValue })
} render() {
return (
<MenuContext.Provider value={{
value: this.state.value,
setValue: this.setValue
}}>
{/* other stuff */}
</MenuContext.Provider>
)
}
}
You Have Two Choices
- Handle mutation yourself 🤡
- Let React do it 😎
You should let React do it.
The only catch is that you’ll be putting something in state that bothers you. It’s okay, you’ll get over it 😬
- Move
setValue
into your state object (and then get over it 😋) - Use
this.state
as the value to Provider.
class Menu extends React.Component {
constructor() {
super()
this.state = {
value: this.props.defaultValue,
setValue: (newValue) => {
this.setState({ value: newValue })
}
}
} render() {
return (
<MenuContext.Provider value={this.state}>
{/* other stuff */}
</MenuContext.Provider>
)
}
}
Now you no longer have to worry about if you changed the identify of the context you provided between renders. React took the mutation wheel.
In short: if all you ever pass to a Provider is this.state
, then you’ll always render responsibly!