Description
Hi there!
I have observed a performance issue with context providers and value updates. I have spoken with @gaearon with this on twitter, so he will have a bit more context
Let's say you have a provider:
const CounterContext = React.createContext(1);
const Provider = ContextContext.Provider
And you update the value to the provider
render() {
return (
return <Provider value={this.state.value}>{this.props.children}</Provider>
)
}
All good so far.
Let's say you want to pass this value down the tree, but for performance reasons you do not want to render the tree. The only components you want to render are your consumer components (in our case CounterContext.Consumer
)
A naive way would be to do something like this:
class Blocker extends React.Component {
shouldComponentUpdate() {
return false;
}
render() {
return this.props.children;
}
}
// ...
render() {
return (
<Provider value={this.state.value}>
<Blocker>
{this.props.children}
</Blocker>
</Provider>
)
}
Even though no components in the tree are rendered except for the consumers, the update itself is very expensive. I suspect that the tree walking algorithm takes a while to run.
Standalone example
https://codesandbox.io/s/61jnr811kr
This example has about a 20-30ms render time for a simple counter update. In a production app with a list of 500 nodes (~500 * 10 components, 5000 components) we were looking at update times similar to that of rendering the tree (150ms+)
A bit more context
I was trying to replace react-redux
with a root StateProvider
that would create a subscription to a store and pass the latest state into the context. The consumers would then pick up this update, run a selector, and re-render if the result value had changed. I had this all working in react-beautiful-dnd
but I found the updates through the context itself was too slow for usage (You can see the relevant files here and here)