-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix error occurring in useEffect
on hmr
#308
base: main
Are you sure you want to change the base?
Fix error occurring in useEffect
on hmr
#308
Conversation
9b897fd
to
e9d1845
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
lgtm, just rewrote the comment slightly for grammar and clarity
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, I forgot to do a thorough review before accepting 😂 Do we also need this in useUpdatableDisposableState
?
If so, there are probably enough places where we do this that it makes sense to have a useEffectIgnoringHmrAndStrictMode
hook.
It might return the lastCommittedParentCache
, in which case we don't need to do anything extra in useCachedResponsivePreCommitValue
@@ -51,6 +51,14 @@ export function useCachedResponsivePrecommitValue<T>( | |||
const lastCommittedParentCache = useRef<ParentCache<T> | null>(null); | |||
|
|||
useEffect(() => { | |||
// react reruns all `useEffect` in HMR since it doesn't know if the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Since React doesn't know if the code inside of a useEffect has changed,
// it unmounts and remounts all useEffect calls when doing HMR. This breaks
// this hook's assumptions! The `onCommit` is called twice.
//
// Since this is a library, the user can't change this code, so we are safe
// to skip this rerun.
//
// This also prevents the useEffect from running twice in Strict Mode.
useEffect(() => { | ||
// react reruns all `useEffect` in HMR since it doesn't know if the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Since React doesn't know if the code inside of a useEffect has changed,
// it unmounts and remounts all useEffect calls when doing HMR. This breaks
// this hook's assumptions! The cleanupFn is called unexpectedly during HMR,
// and the item is not recreated. (Even recreating the item would be a bad outcome,
// as that would reissue network requests.)
//
// Since this is a library, the user can't change this code, so we are safe
// to skip this rerun.
//
// This also prevents the useEffect from running twice in Strict Mode.
|
||
useEffect( | ||
function cleanupItemCleanupPairRefIfSetStateNotCalled() { | ||
if (lastCommittedParentCache.current === parentCache) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// Since React doesn't know if the code inside of a useEffect has changed,
// it unmounts and remounts all useEffect calls when doing HMR. This breaks
// this hook's assumptions! The item in the `itemCleanupPairRef` will be disposed.
//
// Since this is a library, the user can't change this code, so we are safe
// to skip this rerun.
//
// This also prevents the useEffect from running twice in Strict Mode.
Do we also need to have this check above, in the other useEffect
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second one will not throw because it sets the ref to null. However, because of this, we will lose the current state, which is not perfect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, seems better to not set it to null!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh wait, this should be good, because it will cleanup only when we set with a function and when the ref is still not cleaned up, hmr shouldn't be able to trigger it twice
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wdym? Which useEffect are you referring to?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
useEffect(
function cleanupItemCleanupPairRefAfterSetState() {
if (stateFromDisposableStateHook !== UNASSIGNED_STATE) {
if (itemCleanupPairRef.current !== null) {
itemCleanupPairRef.current[1]();
itemCleanupPairRef.current = null;
} else {
throw new Error(
'itemCleanupPairRef.current is unexpectedly null. ' +
'This indicates a bug in react-disposable-state.',
);
}
}
},
[stateFromDisposableStateHook],
);
This one should cleanup only when these two conditions are met, reruning this on hmr has no effect.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I posted this stuff way too quickly. This will throw on hmr but I think just removing the throw should be enough
Rebase of #264