useDeferredValue
useDeferredValue is a React Hook that lets you defer updating a part of the UI.
const deferredValue = useDeferredValue(value)Reference
useDeferredValue(value, initialValue?)
Call useDeferredValue at the top level of your component to get a deferred version of that value.
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}Parameters
value: The value you want to defer. It can have any type.- optional
initialValue: A value to use during the initial render of a component. If this option is omitted,useDeferredValuewill not defer during the initial render, because there’s no previous version ofvaluethat it can render instead.
Returns
currentValue: During the initial render, the returned deferred value will be theinitialValue, or the same as the value you provided. During updates, React will first attempt a re-render with the old value (so it will return the old value), and then try another re-render in the background with the new value (so it will return the updated value).
Caveats
-
When an update is inside a Transition,
useDeferredValuealways returns the newvalueand does not spawn a deferred render, since the update is already deferred. -
The values you pass to
useDeferredValueshould either be primitive values (like strings and numbers) or objects created outside of rendering. If you create a new object during rendering and immediately pass it touseDeferredValue, it will be different on every render, causing unnecessary background re-renders. -
When
useDeferredValuereceives a different value (compared withObject.is), in addition to the current render (when it still uses the previous value), it schedules a re-render in the background with the new value. The background re-render is interruptible: if there’s another update to thevalue, React will restart the background re-render from scratch. For example, if the user is typing into an input faster than a chart receiving its deferred value can re-render, the chart will only re-render after the user stops typing. -
useDeferredValueis integrated with<Suspense>. If the background update caused by a new value suspends the UI, the user will not see the fallback. They will see the old deferred value until the data loads. -
useDeferredValuedoes not by itself prevent extra network requests. -
There is no fixed delay caused by
useDeferredValueitself. As soon as React finishes the original re-render, React will immediately start working on the background re-render with the new deferred value. Any updates caused by events (like typing) will interrupt the background re-render and get prioritized over it. -
The background re-render caused by
useDeferredValuedoes not fire Effects until it’s committed to the screen. If the background re-render suspends, its Effects will run after the data loads and the UI updates.
Usage
Showing stale content while fresh content is loading
Call useDeferredValue at the top level of your component to defer updating some part of your UI.
import { useState, useDeferredValue } from 'react';
function SearchPage() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
// ...
}During the initial render, the deferred value will be the same as the value you provided.
During updates, the deferred value will “lag behind” the latest value. In particular, React will first re-render without updating the deferred value, and then try to re-render with the newly received value in the background.
Let’s walk through an example to see when this is useful.
In this example, the SearchResults component suspends while fetching the search results. Try typing "a", waiting for the results, and then editing it to "ab". The results for "a" get replaced by the loading fallback.
import { Suspense, useState } from 'react'; import SearchResults from './SearchResults.js'; export default function App() { const [query, setQuery] = useState(''); return ( <> <label> Search albums: <input value={query} onChange={e => setQuery(e.target.value)} /> </label> <Suspense fallback={<h2>Loading...</h2>}> <SearchResults query={query} /> </Suspense> </> ); }
A common alternative UI pattern is to defer updating the list of results and to keep showing the previous results until the new results are ready. Call useDeferredValue to pass a deferred version of the query down:
export default function App() {
const [query, setQuery] = useState('');
const deferredQuery = useDeferredValue(query);
return (
<>
<label>
Search albums:
<input value={query} onChange={e => setQuery(e.target.value)} />
</label>
<Suspense fallback={<h2>Loading...</h2>}>
<SearchResults query={deferredQuery} />
</Suspense>