useDebouncedState

Exports Size
loading...
Gzip Size
loading...
Brotli Size
Source Code
View on GitHub
Docs
Edit this page

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 (opens in a new tab) is a new React built-in hook introduced in React 18. It has very different usages and goals than useDebouncedState:

  • useDeferredValue can only be used if you have direct access to the original state value, while useDebouncedState is designed to work when you don't have access to the original state value.
  • useDeferredValue can suppress Suspense fallback from re-appearing when the value changes, which makes it useful in Suspense-based data fetching.
  • useDeferredValue is 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.
  • useDeferredValue are 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. While useDebouncedValue is not interruptible and wouldn’t improve the "lagging" issue.
  • useDeferredValue can not reduce network requests. Though the re-render triggered by the useDeferredValue can 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:

  • useDebouncedState is used for un-controlled components (The defaultValue prop and the onChange prop, no value prop)
  • With useDebouncedState you 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>
  );
}