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
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.
I agree, you can already use a similar hook, which has more options.
https://github.com/xnimorz/use-debounce