I'm trying to use the new Context API in my app and it looks like every time I update the context, it re-renders any component connected to it regardless. I have a sandbox demo setup to see code and working issue. When you type in the input - the buttons context is rendered and vice-versa. My original thinking was that if you type in the input, only the input context would be printed out.
DEMO
Is this how it works or am I missing something? Thanks, Spencer
I'm trying to use the new Context API in my app and it looks like every time I update the context, it re-renders any component connected to it regardless. I have a sandbox demo setup to see code and working issue. When you type in the input - the buttons context is rendered and vice-versa. My original thinking was that if you type in the input, only the input context would be printed out.
DEMO
Is this how it works or am I missing something? Thanks, Spencer
Share Improve this question asked Jul 8, 2018 at 16:13 Spencer BigumSpencer Bigum 1,9313 gold badges20 silver badges30 bronze badges 3- I'm not sure if I understood the problem correctly, but all components that depend on this context are rerendered. It's called ThemeContext. If you update a theme, you would expect that all themed components are updated. – Estus Flask Commented Jul 8, 2018 at 16:30
- Ahh - Ok. So if I don't want the buttons to get re-rendered when the text changes, then do they need to have their own "CONTEXT API"? I'm coming from redux where everything is in the store, and when an item changes the whole store doesn't propagate are-render throughout every connected component. – Spencer Bigum Commented Jul 8, 2018 at 16:34
- 1 Yes, a different context. If you need to have more control over such 'stores', Redux is likely a better option. – Estus Flask Commented Jul 8, 2018 at 16:42
2 Answers
Reset to default 11The way I avoid re-rendering with react context API:
First I write my component as pure functional component:
const MyComponent = React.memo(({
somePropFromContext,
otherPropFromContext,
someRegularPropNotFromContext
}) => {
... // regular component logic
return(
... // regular component return
)
});
Then I write a function to select props from context:
function select(){
const { someSelector, otherSelector } = useContext(MyContext);
return {
somePropFromContext: someSelector,
otherPropFromContext: otherSelector,
}
}
I have my connect HOC wrote:
function connect(WrappedComponent, select){
return function(props){
const selectors = select();
return <WrappedComponent {...selectors} {...props}/>
}
}
All together
import connect from 'path/to/connect'
const MyComponent ... //previous code
function select() ... //previous code
export default connect(MyComponent, select)
Usage
<MyComponent someRegularPropNotFromContext={something} />
Demo
Demo on codesandbox
Conclusion
MyComponent
will re-render only if the specifics props from context updates with a new value, if the value is the same, it will not re-render. Also it avoid re-rendering on any other value from context that is not used inside MyComponent
. The code inside select will execute every time the context updates, but as it does nothing, its ok, since no re-rendering of MyComponent
is wasted.
That is the expected behaviour. Components as consumers re-renders when their provider data updates. Further more, shouldComponentUpdate
hooks do not work on Consumers.
Quoting React's content API:
All Consumers that are descendants of a Provider will re-render whenever the Provider’s value prop changes. The propagation from Provider to its descendant Consumers is not subject to the shouldComponentUpdate method, so the Consumer is updated even when an ancestor component bails out of the update.
For more info check here