By default, React always renders a consistent UI. Usually, when we update the state, we expect to see changes on the screen without any delay. It makes the app feel responsive, giving us a better user experience.
However, sometimes it might be helpful to intentionally introduce an inconsistency.
Consider an example of switching tabs. While navigating from the Home tab to the Profile tab, we might see a blank page with a loader. It is because the profile page data is not loaded.
This becomes very frustrating, isn’t it?
It would have been better to stay on the Home page instead of seeing the blank page.
Before React 18, implementing this pattern was difficult. Thanks to the new concurrent features like startTransition/useTransition, useDeferredValue, etc in React 18, which have made it possible!
Let us check out the newly added hook useDeferredValue
in Concurrent React.
useDeferredValue hook
The useDeferredValue
is a hook that will return a deferred version of the passed value.
It takes in the state value and a timeout in milliseconds.
It is commonly used, to keep the UI responsive when we have something that renders immediately based on user input and something that needs to wait for a data fetch.
Sticking with our example of searching a photo from a large list
discussed in our
previous post on startTransition API,
we will see how we can improve the user experience with the useDeferredValue
hook.
When we type the photo name in the search input, we expect the typed character to appear on the screen without any delay.
But, because of the expensive search operation, the input becomes sluggish, as seen in the image below.
Let us jump into the code.
We need to separate high-priority updates( setPhotoTitle(e.target.value)
)
from low-priority updates( setPhotos(res)
)
to speed up the sluggish inputs.
The useDeferredValue
allows us to do so.
The input should be treated as a high priority task and searching a photo can be reduced to low priority.
Here is the modified code with the useDeferredValue
hook.
In this example, we are deferring the searchParam
and showing the stale results till the new search result is loaded.
We have also added isStale={deferredValue !== title}
to differentiate between old results and new results.
The old results are in grey, while the new results are black.
How is useDefferedValue different from debouncing or throttling?
Debouncing has a fixed artificial delay, no matter how fast our computer is. However, the useDeferredValue value only lags, if the rendering takes a while. There is no minimal lag imposed by React. The lag adjusts to the user’s device. On fast machines, the lag would be less, or non-existent, and on slow devices, it would be more noticeable. In both cases, the app would remain responsive.
NOTE:
The useDeferredValue
hook is experimental and is subject to change.
Read more about the useDeferredValue
hook on
Concurrent UI Patterns documentation
and
WG discussion.