0

As depicted below, I have a basic react context setup.

My desire is child components consuming the context should only re-render when the context data changes, but I'm finding that's not the case.

If I update call setData2, which is not associated with the context, a state update is triggered on Container which subsequently triggers a recalculation and causes the consuming child to update.

The child component in question is already using React.memo, so it would only update if its useContext hook led it to. But the context gets updated when Container updates, even though data1 is unchanged.

Is it possible to fix this?

const Container = () => {
  const [data1, setData1] = useState(...);
  const [data2, setData2] = useState(...);

  return (
    <MyContext.Provider value={{ data1, setData1 }}>
     // child component hierarchy
    </MyContext.Provider>
  );
};
temporary_user_name
  • 35,956
  • 47
  • 141
  • 220

2 Answers2

1

To prevent child components re-rendering when context state changes, you should pass the child components into the context provider via the children prop:

// Define the provider component
const MyContextProvider = ({ children }) => {
  const [data1, setData1] = useState(...);
  const [data2, setData2] = useState(...);

  return (
    <MyContext.Provider value={{ data1, setData1 }}>
     {children}
    </MyContext.Provider>
  );
};

Then somewhere in your app, render the provider:

// Render the provider component
const App = () => {
  return (
    <MyContextProvider>
     // child component hierarchy
    </MyContextProviderr>
  );
};

To access the provider state, a child should use the useContext(MyContext) hook.

There is no need to use React.memo. React knows that when rendering child components using the children prop, the child components do not need to be re-rendered, because it is impossible for them to be affected by local state change.

temporary_user_name
  • 35,956
  • 47
  • 141
  • 220
JMadelaine
  • 2,859
  • 1
  • 12
  • 17
0

You could wrap the child component that you do not want to be rerender (triggered by other unrelated state) in a useMemo with the related dependencies - only to that child

const Container = () => {
  const [data1, setData1] = useState(...);
  const [data2, setData2] = useState(...);

  const rerenderInCertainConditions = useMemo(() => (
    <MyContext.Provider value={{ data1, setData1 }}>
     // child component hierarchy
    </MyContext.Provider>
  ), [data1])

  return (
    {rerenderInCertainConditions}
  );
};

Codesandbox demo

Edit goofy-kare-wgn45

hgb123
  • 13,869
  • 3
  • 20
  • 38