react: useDeferredValue does not abort running reconcile work for stale value

Do you want to request a feature or report a bug? bug

What is the current behavior? When useDeferredValue value is updated while the previous reconcile work is still running, The previous reconcile work is not aborted, and the new work is queued after it.

repro: Based on useDeferredValue example, https://codesandbox.io/s/infallible-dewdney-9fkv9

For better reproducibility i’ve made it to run longer using using timeoutMs: 30000 + while (performance.now() - now < 100) { ) and i’ve added an indicator value: Time since last key stroke https://codesandbox.io/s/intelligent-mestorf-u0p2b

  • Start timeline record
  • Type A B C with 1-3 sec interval between key strokes.
  • See timeline, there’s work of 24 secs 8 + 8 + 8, for each key stroke
  • See the values of Result #X changes to the old stale value.

This is not optimal for 2 reasons:

  • We show stale data when we have something newer.
  • The cpu is locked processing the old value reconcile, and the new value needs to wait.

What is the expected behavior? The prev reconcile work should be cancelled/aborted, And the new value reconcile should start immediately

Which versions of React, and which browser / OS are affected by this issue? Did this work in previous versions of React?

react/react-dom 0.0.0-experimental-5faf377df

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 11
  • Comments: 18

Most upvoted comments

This is probably a bug, same as the issue where multiple startTransitions don’t always use the latest. Let’s track it here.

There’s pretty major ongoing refactors that might end up addressing this. But no promises on the timing. We’ve slipped enough times that I would just say it’s ready when it’s ready. I think we’ll want to fix this before the stable release but it is still a while away.

I think this is fixed. If I missed something, please lmk and I’ll reopen again.

As far as I can see, It’s now working as I would expect. I will try to stress that, but I have good feeling Thank you!

Here’s another version with a console log for clarity. This one uses 50 items with 10ms delay (x2 because Strict Mode) = ~ 1 second per list render.

I think this behavior makes sense.

https://codesandbox.io/s/usedeferredvalue-issue-iteration-forked-ec63h?file=/src/MySlowList.js

https://user-images.githubusercontent.com/810438/112223433-d62cc600-8c21-11eb-8963-d80924360e59.mov

You can see that we’re generally only trying to render the most recent value. So if you type something else we immediately invalidate and don’t waste time trying to render stale ones. If we manage to finish before you type something else, we show it asap. If we don’t manage to finish, we restart when you type a new character. If you keep on typing without stopping, we keep restarting until 5 seconds pass, at which point we force it to be flushed. So you have a hang but the screen updates.

Is this behavior closer to what you had in mind?

any ETA on this @gaearon ? We really want to refactor for concurrent mode support. This is holding us down.

Maybe the name useDeferredValue should be renamed to useDebouncedValue or allow us to pass an option to archive desired behavior.

I agree, you can already use a similar hook, which has more options.

https://github.com/xnimorz/use-debounce