dai-shi/use-context-selector

components using useContextSelector have to be wrapped in memo to prevent surplus re-renders

laurencefass opened this issue · 9 comments

components using use-context-selector have to be wrapped in memo to prevent surplus re-renders

demo here:

https://codesandbox.io/s/react-use-context-selector-mn8sv?file=/src/App.js

Is this by design, or is it a bug? I cant see any reason why the updates should occur in the non-memoized component.

This is not the case with Redux as components using useSelector do not have to be wrapped by default.

e.g. https://codesandbox.io/s/rtk-counter-53y5q

Thanks

It's how React works. When a component re-renders, all children re-render.
This is the pattern to avoid memo.
https://codesandbox.io/s/react-use-context-selector-forked-hp59j?file=/src/App.js
(It's the pattern not only for use-context-selector but for context in general.)

Thanks for feedback.

May I ask: why do you not conceal the useCallback hook call inside your useContextSelector function so its not a requirement on the consumer? It doesnt work properly without it.

i.e. change from

const counter1 = useContextSelector(
    context,
    useCallback((state) => state.state.counter1, [])
  );

to

// the wrapped function exposed to the package consumer
function useContextSelector_wrapped(context, callback) {
  return useContextSelector_existing(context,  useCallback(callback, [])
}

to conceal the additional useHook, and then use it as follows to match Redux pattern

const counter1 = useContextSelector_wrapped(context, (state) => state.state.counter1));

I guess my question is why do you expose the additional useHook call to the function consumer?

thanks

Oops, sorry, you don't need stable selectors for this lib.
You can just use inline selectors (= no useCallback).

Apologies but I dont understand that statement. What is inline selector and how do i use it can you include example?
Thanks

Im confused!

I deleted the first sandbox so cant refer back to it. Whats the difference between the op sandbox and the last one you posted. It appears to be working fine. ?

The OP sandbox is

const App = () => {
  const [state, setState] = useState(...)
  return (
    <Context.Provider ...>
      ...
    </Context.Provider>
  )
}

When App re-renders all children re-render.
Again, it has nothing to do with how use-context-selector is implemented.
This is a tricky part of how React works/optimizes re-renders.
Maybe this article helps? https://overreacted.io/before-you-memo/

solved! thanks for your help