Non-beta release React 18 included two new React hooks to increase performance with control rendering slow content. The useDeferredValue hook is one of those hooks that are simple to use but tricky to comprehend. In this article, I’ll describe how it works and when you should use it.
Consider the following code first:
const ListItems= () =>{
const [txtValue, setTxtValue] = useState("")
const expensiveFunction = useMemo(() => {
return getComplexValue(txtValue)
}, [txtValue])
function handleChange(e) {
setTxtValue(e.target.value)
}
return <input type="text" value={txtValue} onChange={handleChange} />
}
This code is a clear component including a state and an expensiveFunction variable whose state has side effects on it. Every once the input value is changed, the name state is updated, and the entire component re-renders after state updating is done. ExpensiveFunction is recalculated during rendering due to name state.
This may not appear to be a problem at first glance, but the code inside useMemo is computationally costly, and performance issues could occur.
const ListItems= () =>{
const [txtValue, setTxtValue] = useState("")
const list = useMemo(() => {
return largeList.filter(item => item.name.includes(txtValue))
}, [txtValue])
function handleChange(e) {
setTxtValue(e.target.value)
}
return <>
<input type="text" value={txtValue} onChange={handleChange} />
{list.map(item => <Item key={item.id} item={item} />)}
</>
}
Imagine There is a large list that comes from the API and uses name state to filter list items. Filtering through long lists and rendering them to the screen takes time and will be a slow process every time the input values change. It is a performance issue that makes the user interface sluggish to use.
When you try to type into the input box, as shown in this instance, it is extremely slow, taking about a second to update the input box. Due to rendering and processing, the list takes so long. It is where useDeferredValue comes into play here.
Explain useDeferredValue
The useDeferredValue hook solves the slow render bug by adding a delay before some information is estimated. This works similarly to debouncing and throttling in that our deferred value is calculated only after the important state updates have been completed.
const ListItems= () =>{
const [txtValue, setTxtValue] = useState("")
const deferredName = useDeferredValue(txtValue)
const list = useMemo(() => {
return largeList.filter(item => item.name.includes(deferredName))
}, [deferredName])
function handleChange(e) {
setTxtValue(e.target.value)
}
return <>
<input type="text" value={txtValue} onChange={handleChange} />
{list.map(item => <Item key={item.id} item={item} />)}
</>
}
We fixed this problem in the above example by using the useDeferredValue hook. It accepts a single parameter, which is the value on which you want to configure your throttle/debounce. It will return the deferred version of the value you passed in. It means that even if our name variable changes, the deferredName will remain unchanged because useDeferredValue doesn't instantly update the deferredName's value.
This gives our component enough time to completely render with the new name value because our list will not attempt to update itself while waiting for the deferredName to change. This causes the app to be more responsive because input updates instantly whereas the list updates later.
The reason the list is delayed is we changed our useMemo code to rely on the deferredName instead of the actual name. This means that if we change the name our deferredName will wait to update until after the UI has had time to update with the new name value in the input field. If we change our input in a short time (for example, by typing quickly in the input), the deferredName value will continue to stay unchanged and our list will not update. The only thing that will update is the name variable until there is a pause in the name value changing. Once we stop typing, React will update the deferredName value with the most recent name value and re-render the list.
In the above example, the list behaves as expected when using the useDeferredValue hook. When you type, you can see that the input quickly updates, but the list itself does not. The old value of the list remains on the screen until there is a sufficient delay for it to render itself out to the screen.
useDeferredValue hook is simple to use, but it isn't something you would like to use all the time. You should only use it if you are experiencing performance issues with your code and have exhausted all other options. If you use it all the time, your app will become less performant because React will be forced to perform extra renders of your app. This happens because it will perform both the initial and deferred re-render when it could have done both in a single update without useDeferredValue.
As a result, the useDeferredValue hook makes it a lot simpler to work with slow, compute-complex re-renders because we can now tell React to defer those updates until later, making your application appear much more performant to users.
Top comments (3)
Very usefull
tnx for this useful post.
Praktisch