useDebouncedState
Debounces state changes of un-controlled components. This can be useful in case you want to perform a heavy operation based on react state, for example, send a search request.
Differences with useDeferredValue
useDeferredValue is a new React built-in hook introduced in React 18. It has very different usages and goals than useDebouncedState:
useDeferredValuecan only be used if you have direct access to the original state value, whileuseDebouncedStateis designed to work when you don’t have access to the original state value.useDeferredValuecan suppress Suspense fallback from re-appearing when the value changes, which makes it useful in Suspense-based data fetching.useDeferredValueis better suited to optimizing rendering because it is deeply integrated with React itself and adapts to the user’s device as it doesn’t require choosing any fixed delay. The deferred re-render would happen almost immediately and wouldn’t be noticeable if the user’s device is fast (e.g. powerful laptop), while the re-render could “lag behind” the user interaction proportionally to how slow the device is.useDeferredValueare interruptible by default, e.g. if React is in the middle of re-rendering a large list, but the user makes another input, React will abandon the current re-render, handle the keystroke, and then start rendering in the background again. WhileuseDebouncedValueis not interruptible and wouldn’t improve the “lagging” issue.useDeferredValuecan not reduce network requests. Though the re-render triggered by theuseDeferredValuecan be interrupted by user interaction, the returned JSX from the re-render can be discarded by React (due to being outdated during Concurrent Rendering), the re-render itself will always happen and the network request will always be fired.
Differences with useDebouncedValue
useDebouncedValue is another hook provided by foxact. However, it is designed to work with controlled components:
useDebouncedStateis used for un-controlled components (ThedefaultValueprop and theonChangeprop, novalueprop)- With
useDebouncedStateyou can not have direct access to the original state value.
Usage
Below is an example of different use cases of useDebouncedState and useDeferredValue, and how they can be used together:
import { useDeferredValue } from 'react';
import useSWR from 'swr';
import { useDebouncedState } from 'foxact/use-debounced-state';
const Example = () => {
const [debouncedValue, debouncilySetState] = useDebouncedState(
// The initial value or a pure function that will return the initial value, just like the useState
initialValue,
// delay in ms
300,
// optional, default to false. whether to immediately update the debounced value with the first call
false
);
// The debounced value can reduce network requests.
const { data } = useSWR<string[]>(debouncedValue ? `/api/search?q=${debouncedValue}` : null);
// useDeferredValue here is used to opt-in the Concurrent Rendering, to improve the UX.
// The value passed to useDeferredValue must be primitive values or memoized. Here we
// are relying on useSWR to memoize the returned `data` (which will be an array).
const deferredData = useDeferredValue(data);
return (
<div>
<input
// The <input /> here is not controlled by React as we are not using the `value` prop.
defaultValue={initialValue}
// Although the `onChange` will be fired on every keystroke, the state update will be
// debounced
onChange={useCallback((e) => debouncilySetState(e.target.value), [debouncilySetState])}
/>
{/**
* <FancyYetSlowSearchResult /> needs to be wrapped in React.memo, so that it will
* only re-render when the `deferredData` changes, not when the `debouncedValue` changes.
*/}
<FancyYetSlowSearchResult
// The re-render of <FancyYetSlowSearchResult /> will be interruptible, as it will
// be triggered by `useDeferredValue`.
// So <FancyYetSlowSearchResult /> may be slow to render, the entire UI will always
// remain responsive.
list={deferredData}
/>
</div>
);
}