Open mischnic opened 3 years ago
Curious what the reason is for deferring the assignment of valueRef.current
into the layout effect callback - why not just assign it unconditionally in Provider()
?
Regarding the stale closure - it seems like the useReducer usage here is incorrect - the reducer function passed to useReducer references the enclosed value
binding from the first execution of useContextSelector(), since useReducer's reducer reference is considered immutable. A corrected version would be:
function useContextSelector(context) {
const contextValue = useContextOrig(context);
const [state, dispatch] = useReducer(
() => {
return {
value: contextValue.value.current,
};
},
{
value: contextValue.value.current,
}
);
useLayoutEffect(() => {
contextValue.listener = dispatch;
}, []);
return state.value;
}
I didn't write this code, but apparently it's based on the principle explained here: https://overreacted.io/a-complete-guide-to-useeffect/#why-usereducer-is-the-cheat-mode-of-hooks where local variables are used as well instead of a ref. (https://github.com/dai-shi/use-context-selector/pull/36#issuecomment-754554120)
It looks like React defers calling the reducer
until the component has rendered which we don't i.e. dispatch
will queue up that invocation rather than immediately invoke it like we do in Preact. That seems to be the main difference here, this is similar to the explanation in https://github.com/preactjs/preact/issues/2750#issuecomment-699101628
Reproduction
https://codesandbox.io/s/nostalgic-wilson-3vs0s
Steps to reproduce
Click on the "+1" button multiple times.
Expected Behavior
What React does: both counters increment simultaneously.
Actual Behavior
With Preact, the first update is ignored and the "local counter" is offset by one to the "global counter".
Came up in https://github.com/dai-shi/use-context-selector/pull/36
cc @marvinhagemeister @dai-shi