React Context API Seems to re-render every component
Asked Answered
T

2

5

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

Tempestuous answered 8/7, 2018 at 16:13 Comment(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.Daphie
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.Tempestuous
Yes, a different context. If you need to have more control over such 'stores', Redux is likely a better option.Daphie
R
7

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

Replay answered 8/7, 2018 at 16:30 Comment(5)
Thank you - so based on this - I must need a 2nd context api so the buttons don't get re-rendered when the text changes....Tempestuous
Or you can use the shouldComponentUpdate() hook to determine whether or not you need to rerender. This should fix your problem right?Replay
Ok that did help but didn't do exactly what I was hoping for, however this did help clear a lot of things up. I was just expecting it work more like Redux for some reason. Thank you!Tempestuous
I just updated my answer. IF you deem my answer is correct, feel free to accept itReplay
Thanks so much again. I ended up writing it in redux only to get to the same point haha. But I learned a ton along the way getting into the nitty gritty of context api. The ShouldComponentUpdate was a huge help in getting the behavior rendering similar to Redux - although with Redux its free - but good to know none the less. CheersTempestuous
B
11

The 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.

Boogiewoogie answered 16/4, 2019 at 17:8 Comment(6)
doesn't that goes against what Context API is supposed to do? might as well use Redux, since you're creating your own "connect" function... Two problems persist by adding a connector: Wrapper Hell and Props DrillingLunitidal
Battle of the PedrosFolsom
Gives error for me: React Hook "..." is called in function "select" which is neither a React function component or a custom React Hook functionTrengganu
@HasanSefaOzalp you are probably doing something wrong, maybe not importing the select function where you are using it.Boogiewoogie
This is some solution but doesn't answer the question. The answer is that no matter what it won't bailout any components from the provider to the consumer.Serous
Use redux... immer makes it better than contextKirimia
R
7

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

Replay answered 8/7, 2018 at 16:30 Comment(5)
Thank you - so based on this - I must need a 2nd context api so the buttons don't get re-rendered when the text changes....Tempestuous
Or you can use the shouldComponentUpdate() hook to determine whether or not you need to rerender. This should fix your problem right?Replay
Ok that did help but didn't do exactly what I was hoping for, however this did help clear a lot of things up. I was just expecting it work more like Redux for some reason. Thank you!Tempestuous
I just updated my answer. IF you deem my answer is correct, feel free to accept itReplay
Thanks so much again. I ended up writing it in redux only to get to the same point haha. But I learned a ton along the way getting into the nitty gritty of context api. The ShouldComponentUpdate was a huge help in getting the behavior rendering similar to Redux - although with Redux its free - but good to know none the less. CheersTempestuous

© 2022 - 2024 — McMap. All rights reserved.