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
Result #X
changes to the old stale value.This is not optimal for 2 reasons:
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
I agree. I thinkuseDeferredValue
should work more like debounce(..., {leading: false, trailing: true})
that cancels processing when there are new changes in the time-window.
Maybe the name useDeferredValue
should be renamed to useDebouncedValue
or allow us to pass an option to achieve desired behavior.
In hour case we have a page with a huge dynamic table that is controlled by many filters. The table might contain 100+ of rows and 20+ of columns. Each cell is connected by context and then react lags a lot when some of the filters like checkboxes, text search field, change fast by user input.
To optimize this we have debounced the input components and the render props off the table itself by using podefr/react-debounce-render.
This is a great case where I hoped using Suspense
and useDeferredValue
would help us eliminate the need for this hacks both in inputs controls and table render props.
Thank you for the great work.
This is probably a bug, same as the issue where multiple startTransitions don’t always use the latest. Let’s track it here.
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.
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.
Actually, there are some distinctions between useDebounce and useDeferredValue. From the React Docs:
A common way to work around the stutter is to “debounce” the input. When debouncing, we only update the list after the user stops typing. However, it can be frustrating that the UI doesn’t update while we’re typing.
In order to demonstrate this difference I took take this example from the React Docs: https://codesandbox.io/s/infallible-dewdney-9fkv9, forked it, and changed the performance lag from 3ms to 1ms. Then I made one example with useDeferredValue and one with useDebounce.
useDeferredValue: https://codesandbox.io/s/tender-cray-rht7e
useDebounce: https://codesandbox.io/s/distracted-gauss-19fu8
As you can see, if you type into the useDebounce on a fast computer the text won't re-render until you are done typing, but with useDeferredValue it re-renders immediately.
So, the challenge would be to add an option that allows for cancelling of stale values, but still allows updating to happen as you type on fast machines.
I definitely think having an option to "abort reconcile work for stale values" is important. The behavior of showing every intermediate state and having to wait for every intermediate state to be calculated and rendered before showing the final state is a very bad UX for many cases where useDeferredValue would otherwise be helpful - in a filtered list for example. But maybe the cancellation behavior is something the programmer has to opt into or at least can opt out of and maybe it should only cancel stale values after a short timeout, so that on fast machines we still have don't have to wait for the user to stop typing before making updates.
As you can see, if you type into the useDebounce on a fast computer the text won't re-render until you are done typing, but with useDeferredValue it re-renders immediately.
Did you try the useDebounce maxWait option? It's similar to maxWait used by lodash debounce?
Dan wrote it's looks like not the expected behaviour, and probably a bug
I've wait for his updates
anyhow, it's still persists on recent experimental react
https://codesandbox.io/s/usedeferredvalue-issue-iteration-i7tkm
As you can see, if you type into the useDebounce on a fast computer the text won't re-render until you are done typing, but with useDeferredValue it re-renders immediately.
Did you try the useDebounce maxWait option? It's similar to maxWait used by lodash debounce?
I had not until just now. Using maxWait helps and useDebounce could be the right solution for many or even most applications, but even with maxWait there will still be uses where useDeferredValue can be more responsive.
I am using a maxWait of 1000ms in these examples. In the case of a fast list, the text underneath the input lags noticeably. In the case of a slow list, the input stutters.
Slow List
useDeferredValue: https://codesandbox.io/s/gifted-payne-05mbm
useDebounce with maxWait: https://codesandbox.io/s/musing-dijkstra-h0izp
Fast List
useDeferredValue: https://codesandbox.io/s/tender-cray-rht7e
useDebounce with maxWait: https://codesandbox.io/s/happy-chebyshev-tyw7p
In this case I would still prefer the useDebounce to the useDeferredValue until we can cancel reconcile work. In the slow example, if I delete hello, type hello again, delete again, and type it again, I have to watch as the text below lags behind in copying everything I have done. So if useDeferredValues is going to be used for inputs there really ought to at least be an option to cancel work for stale values, if not have the cancellation be default behavior.
Dan wrote it's looks like not the expected behaviour, and probably a bug
I've wait for his updates
anyhow, it's still persists on recent experimental react
https://codesandbox.io/s/usedeferredvalue-issue-iteration-i7tkm
Hope we see this bug fixed in the next update!
This appears to be fixed on master.
https://codesandbox.io/s/elated-tu-364cq
If not please file a new issue with repro instructions.
The sandbox in your link gives even worse behaviour than before
The intermediate states are still committed, work is not aborted, and the UI is blocked for new key presses
See video:
https://www.youtube.com/watch?v=JnVcQanK9OA
Ah, I think I see what you mean. The update seems to "expire" causing a sync evaluation but it (probably) shouldn't because the timeout is large.
Thanks :)
any ETA on this @gaearon ? We really want to refactor for concurrent mode support. This is holding us down.
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.
Most helpful comment
This is probably a bug, same as the issue where multiple startTransitions don’t always use the latest. Let’s track it here.