假设有如下这么一个需求:有一个输入框,每次输入时会根据value去发送请求,需要限制请求频率。
然后使用react hook,第一次写很容易出错:
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。这是因为函数式组件每次渲染时,组件内的变量都会被释放掉重新初始化。
想要做到搜索防抖,可以写一个自定义钩子:
export function useDebounceState(state, duration = 1000) {
const [debounceState, setDebounceState] = useState(state)
useEffect(() => {
const timer = setTimeout(() => {
setDebounceState(state)
}, duration)
// 每次组件卸载时都会执行返回的这个函数
return () => clearTimeout(timer)
}, [state])
return debounceState
}
然后使用它:
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
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属性拿到初始值。
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>
)
}