React Hook 中实现 debounce 防抖函数
假设有如下这么一个需求:有一个输入框,每次输入时会根据 value 去发送请求,需要限制请求频率。
然后使用 react hook,第一次写很容易出错:
js
import _ from 'lodash'
import { useEffect, useState} from 'react'
export function SearchBar() {
const [searchValue, setSearchValue] = useState('')
useEffect(_.debounce(() => {
// fetch('https://...')
}, 500), [searchValue])
return (
<input type="text" value={searchValue} onChange={e => setSearchValue(e.target.value)}></input>
)
}然后发现每次 searchValue 改变时,所有请求都是延后 500ms 后触发,并没有做到 debounce。这是因为函数式组件每次渲染时,组件内的变量都会被释放掉重新初始化。
想要做到搜索防抖,可以写一个自定义钩子:
js
export function useDebounceState(state, duration = 1000) {
const [debounceState, setDebounceState] = useState(state)
useEffect(() => {
const timer = setTimeout(() => {
setDebounceState(state)
}, duration)
// 每次组件卸载时都会执行返回的这个函数
return () => clearTimeout(timer)
}, [state])
return debounceState
}然后使用它:
js
export function SearchBar() {
const [searchValue, setSearchValue] = useState('')
const deboucneValue = useDebounceState(searchValue, 500)
useEffect(() => {
// fetch('https://...')
// 第一次这里就会执行
}, [deboucneValue])
return (
<input type="text" value={searchValue} onChange={e => setSearchValue(e.target.value)}></input>
)
}然后还有两种比较简单的方法,使用 useCallback 钩子或者 useRef 钩子。
useCallback
js
export function SearchBar() {
const [searchValue, setSearchValue] = useState('')
// 只有当依赖项变化时,返回的函数引用才会改变。因为传入的 [], 所以函数引用永远不会变更。
const delayFetch = useCallback(_.debounce((param) => {
// fetch('https://...')
}, 500), [])
function handleChange(e) {
setSearchValue(e.target.value)
delayFetch(e.target.value)
}
return (
<input type="text" value={searchValue} onChange={handleChange}></input>
)
}useRef 会在函数式组件重新渲染时返回一个相同的 ref 对象。可以通过.current 属性拿到初始值。
js
export function SearchBar() {
const [searchValue, setSearchValue] = useState('')
const delayFetch = useRef(_.debounce((param) => {
// fetch('https://...')
}, 500)).current
function handleChange(e) {
setSearchValue(e.target.value)
delayFetch(e.target.value)
}
return (
<input type="text" value={searchValue} onChange={handleChange}></input>
)
}