Skip to content
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

Callback may be stale when called #8

Open
vepasto opened this issue Dec 23, 2021 · 4 comments
Open

Callback may be stale when called #8

vepasto opened this issue Dec 23, 2021 · 4 comments

Comments

@vepasto
Copy link

vepasto commented Dec 23, 2021

This causes that stale callback may be called:

useEffect(() => {
	savedCallback.current = callback;
}, [callback]);

Easy fix is to just remove useEffect and assign callback immediately:

savedCallback.current = callback;
@johndiiorio
Copy link
Owner

Hello, thanks for opening an issue. I believe that the useEffect is necessary so that savedCallback is not updated until have the render occurs, as describe in this blog post.

Would it be possible for you to provide a CodeSandbox link that reproduces your issue?

@vepasto
Copy link
Author

vepasto commented Dec 24, 2021

Hi, the blog post did not clarify me why it needs to have that useEffect there.

Here is CodeSandbox link https://codesandbox.io/s/react-typescript-playground-forked-09rdd?file=/src/index.tsx

In CodeSandbox wait for a while and then press stop button.
First Count and RealCount represents the current implementation and the second ones represents implementation without useEffect.

@johndiiorio
Copy link
Owner

Thanks for the CodeSandbox link. This is definitely an subtle bug. After playing around with your example, I could prevent the issue by making one of the following changes:

  • Changing the setCount to be in the updater form of setState in intervalCallback
  • Removing one of the two useInterval calls in Counter
  • Increasing the interval delay
  • Switching the current useInterval implementation to use a useLayoutEffect instead of a useEffect
  • Setting the savedCallback ref directly in the render

While I'm not 100% sure, I think this has to do with the order of which React batches setState updates. Other useInterval implementations also seem to have this issue. I'm not sure if changing it so that the ref is updated in the render, or if changing the useEffect to a useLayoutEffect is better - there does seem to be a behavioral difference.

@vepasto
Copy link
Author

vepasto commented Jan 3, 2022

  • Changing the setCount to be in the updater form of setState in intervalCallback
  • Removing one of the two useInterval calls in Counter
  • Increasing the interval delay

The example code is just to emphasize the problem, not to represent any kind of real world application.

  • Switching the current useInterval implementation to use a useLayoutEffect instead of a useEffect

I don't see point delaying value assign. I can't think of real life application where we would need to wait for DOM render before we assign the value.

  • Setting the savedCallback ref directly in the render

In my opinion this would be the best solution

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants