JotaiJotai

Jotai
React 原始而灵活的状态管理

atomWithDebounce

atomWithDebounce帮助创建一个状态设置应被防抖的原子。

这个工具对于文本搜索输入很有用,你可能希望在等待一段时间后只调用一次派生原子中的函数,而不是在每次按键时触发一个动作。

import { atom, SetStateAction } from 'jotai'
export default function atomWithDebounce<T>(
initialValue: T,
delayMilliseconds = 500,
shouldDebounceOnReset = false,
) {
const prevTimeoutAtom = atom<ReturnType<typeof setTimeout> | undefined>(
undefined,
)
// 不要导出currentValueAtom,因为使用这个原子来设置状态可能会导致
// currentValueAtom和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(prevTimeoutAtom, nextTimeoutId)
},
)
// 导出原子设置器以便在需要时清除超时
const clearTimeoutAtom = atom(null, (get, set, _arg) => {
clearTimeout(get(prevTimeoutAtom))
set(isDebouncingAtom, false)
})
return {
currentValueAtom: atom((get) => get(_currentValueAtom)),
isDebouncingAtom,
clearTimeoutAtom,
debouncedValueAtom,
}
}

注意事项

请注意,这个原子与React 18中的并发特性如useTransitionuseDeferredValue有不同的目标,后者的主要目标是防止因昂贵的更新而阻塞与页面的交互。

更多信息,请阅读这个github讨论https://github.com/reactwg/react-18/discussions/41 ,在标题为 "How is it different from setTimeout?" 的部分。

示例用法

下面的沙箱链接展示了我们如何使用派生原子来根据debouncedValueAtom的值获取状态。

<SearchInput>中输入一个宝可梦的名字时,我们不会在每个字母上发送一个get请求,而只有在最后一次文本输入后delayMilliseconds已经过去。

这减少了对服务器的后端请求的数量。