prevent child component to re-render below context provider with memo
Asked Answered
C

1

9

I am using a context provider in React to share data across several components. However since a value gets changed from one of my subcomponents, it rerenders all of my other components which partly leads to performance issues. So I want to prevent my child components to rerender. I tried using React.memo() but it's still rendering whenever I set the state of the Context Provider.

const Authenticator = React.memo(() => {
  
  const [myChat, setMyChat] = useContext(ChatContext);

  console.log("rerender"); // gets called everytime on click
  return (
    <Button
      title="click me"
      onPress={() => setMyChat({ text: "hello" })}
    ></Button>
  );
});

My Context Provider looks like this:

const ChatProvider = ({ children }) => {
  const [myChat, setMyChat] = useState([]);

  return (
    <ChatContext.Provider value={[myChat, setMyChat]}>
      {children}
    </ChatContext.Provider>
  );
};

My App.js looks like this:

<ChatProvider>
  <Authenticator />
</ChatProvider>
Copyright answered 5/1, 2021 at 2:50 Comment(0)
E
8

React.Memo doesn't help since you are calling the useContext hook which will cause the component to re-render every time the value from the provider changes. You should consider splitting your context into two separate contexts: one for the value, one for the state updater.

const ChatProvider = ({ children }) => {
  const [myChat, setMyChat] = useState([])

  return (
    <ChatDispatchContext.Provider value={setMyChat}>
      <ChatValueContext.Provider value={myChat}>
        {children}
      </ChatValueContext.Provider>
    </ChatDispatchContext.Provider>
  )
}

Then, update your Authenticator component to the following:

const Authenticator = React.memo(() => {
  const setMyChat = useContext(ChatDispatchContext)

  return (
    <Button
      title="click me"
      onPress={() => setMyChat({ text: "hello" })}
    ></Button>
  )
})
Efrainefram answered 5/1, 2021 at 2:58 Comment(6)
thank you for your answer. I am still not 100% sure I understood, because 1) how can I set my state from the Dispatch Provider in my ValueContext? 2) since the Dispatch Provider is above the Value Provider, it would also rerender the children? CanKoopman
1. Calling setMyChat will update the state value in ChatProvider which will be passed to your components consuming ChatDispatchContext. 2. The order of the providers doesn't matter. What matters is that your Authenticator component is using a context value which doesn't change (setMyChat). This combined with React.memo will prevent the component from re-rendering.Efrainefram
Check this video: youtube.com/watch?v=5gUHfe-ETuo&ab_channel=BenAwadTerrorize
Is splitting into two contexts the only way @MarkSkelton? No chance to get them into the same Provider? Tried to wrap the context's methods with useCallback but no luck.Cassation
I believe you can do "const contextValue = React.useMemo(()=>[myChat, setMyChat], [myChat, setMyChat])" and pass on contextValue to the provider. That would only update the context whenever myChat changesLicense
Look at the answer here: github.com/facebook/react/issues/15156Kendricks

© 2022 - 2024 — McMap. All rights reserved.