Skip to main content
Module

x/jotai/docs/recipes/atom-with-debounce.mdx

👻 Primitive and flexible state management for React
Go to Latest
File
---title: atomWithDebouncenav: 8.10keywords: creators,debounce---
> `atomWithDebounce` helps with creating an atom where state set should be debounced.This util is useful for text search inputs, where you would like to call **functions in derived atoms only once** after waiting for a duration, instead of firing an action on every keystroke.
```tsximport { atom, SetStateAction } from 'jotai'
export default function atomWithDebounce<T>( initialValue: T, delayMilliseconds = 500, shouldDebounceOnReset = false,) { const prevTimeoutAtom = atom<ReturnType<typeof setTimeout> | undefined>( undefined, ) // DO NOT EXPORT currentValueAtom as using this atom to set state can cause // inconsistent state between currentValueAtom and debouncedValueAtom const _currentValueAtom = atom(initialValue) const isDebouncingAtom = atom(false) const debouncedValueAtom = atom( initialValue, (get, set, update: SetStateAction<T>) => { clearTimeout(get(prevTimeoutAtom)) const prevValue = get(_currentValueAtom) const nextValue = typeof update === 'function' ? (update as (prev: T) => T)(prevValue) : update const onDebounceStart = () => { set(_currentValueAtom, nextValue) set(isDebouncingAtom, true) } const onDebounceEnd = () => { set(debouncedValueAtom, nextValue) set(isDebouncingAtom, false) } onDebounceStart() if (!shouldDebounceOnReset && nextValue === initialValue) { onDebounceEnd() return } const nextTimeoutId = setTimeout(() => { onDebounceEnd() }, delayMilliseconds) // set previous timeout atom in case it needs to get cleared set(prevTimeoutAtom, nextTimeoutId) }, ) // exported atom setter to clear timeout if needed const clearTimeoutAtom = atom(null, (get, set, _arg) => { clearTimeout(get(prevTimeoutAtom)) set(isDebouncingAtom, false) }) return { currentValueAtom: atom((get) => get(_currentValueAtom)), isDebouncingAtom, clearTimeoutAtom, debouncedValueAtom, }}```
### Caveat
Please note that this atom has different objectives from concurrent features in React 18 such as `useTransition` and `useDeferredValue` whose main aim is to prevent blocking of interaction with the page for expensive updates.
For more info, please read this github discussion https://github.com/reactwg/react-18/discussions/41 under the section titled **"How is it different from setTimeout?"**
### Example Usage
The sandbox link below shows how we would use a derived atom to fetch state based on the value of `debouncedValueAtom`.
When typing a pokemon's name in `<SearchInput>`, we do not send a get request on every letter, but only after `delayMilliseconds` has passed since the last text input.
This reduces the number of backend requests to the server.
<CodeSandbox id="cjrz2y" />