简体   繁体   中英

React Context will always re-render

If you declare an object within the context Provider value any context consumers will always re-render if the provider re-renders, because each time provider re-renders the object inside value is re-declared.

return (
  <Provider value={{ /* declared object */ }}>{children}</Provider>
);

Good Provider value Pattern

This below is a good pattern, because store will only change when setStore has been called.

// ParentComponent.js

function ParentComponent() {
  const [store, setStore] = useState({ bool: false });

  return (
    <Context.Provider value={store}>
      <button onClick={() => setStore({ ...store, n: { bool: !store.n.bool } })}>
        Switch Bool
      </button>
      <ContextConsumingComponent />
    </Context.Provider>
  );
}

// ContextConsumingComponent.js
function ContextConsumingComponent() {
  const store = useContext(Context);

  console.log('Will only run re-render if the `Switch Bool` button is clicked in parent.');

  return <p>{JSON.stringify(context.bool)}</p>;
}
// Avoid re-renders if props haven't changed.
export default React.memo(ContextConsumingComponent);

Bad Provider value Pattern

This is a bad pattern, because <Provider value={{}} /> is listening for changes to the enclosing object, rather than the store and setStore references themselves. The reality is we only ever want to really listen to store anyway.

// ParentComponent.js

function ParentComponent() {
  const [store, setStore] = useState({ bool: false });


  return (
    <Context.Provider value={{ store, setStore }}>
      <button onClick={() => setStore({ ...store, n: { bool: !store.n.bool } })}>
        Switch Bool
      </button>
      <ContextConsumingComponent />
    </Context.Provider>
  );
}

Question:

I'd like to be be able to use setStore in my context consuming components and I'd like to understand how other people approach this. I believe I have an option of two providers options but interested to see how others approach this and If I've missed any ±'s.

Two Providers Option: store and dispatch

// Two Providers
function ParentComponent() {
  const [store, setStore] = useState({ bool: false });


  return (
    <Store.Provider value={store}>
      <Dispatch.Provider value={setStore}>
        <button onClick={() => setStore({ ...store, n: { bool: !store.n.bool } })}>
          Switch Bool
        </button>
        <ContextConsumingComponent />
      </Dispatch.Provider>
    </Store.Provider>
  );
}

The answer to my question is likely useMemo .

function App() {
  const [store, setStore] = useState({ bool: false });

  const memoized = useMemo(() => ([store, setStore]), [store, setStore]);


  return (
    <RCTX.Provider value={memoized}>
        <button onClick={() => setStore({ bool: !store.bool })}>
            Switch Bool
        </button>
        <SubComponent />
    </RCTX.Provider>
  );
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM