本文由 简悦 SimpRead 转码, 原文地址 mp.weixin.qq.com
前言—
本文通过实现 useDebounceFn、useDebounce、useDebounceEffect 3 种自定义防抖 Hooks,来介绍在日常开发过程中自定义 Hooks 的思路及实现,帮助大家完成通用 Hooks 来提高开发效率。
防抖—
防抖的概念已经司空见惯了,这里稍作简单介绍:
举例:输入事件,原本连续输入 10 个字符会触发 10 次事件,使用防抖可以在间断输入时才触发一次事件
防抖:某连续事件可通过防抖定义为超过指定时间事件才触发
一个简单的防抖可以通过闭包 + 定时器实现:
function debounce(fn, delay) { let timer = null return function (...args) { // 下一次触发时清空定时器,这样事件就要等新的定时器结束后才执行了 if (timer) { clearTimeout(timer) } timer = setTimeout(() => { fn.call(this, ...args) }, delay) }}在 React 函数组件内防抖—
真实场景
比如有一个这样的需求,是在更新 Input 输入框时,搜索数据。
类似的功能图如下:
通过 React useEffect,简单的 demo 实现如下:
import { Input } from 'antd'import { useEffect, useState } from 'react'import './App.css'function App() { const [val, setVal] = useState('') const onSearch = (val) => { console.log('搜索', val || '全部') } const onChange = (e) => setVal(e.target.value) // 当 val 发生变化时,请求搜索数据 useEffect(() => { onSearch(val) }, [val]) return ( <div className='App'> <Input value={val} placeholder='请输入' onChange={onChange} allowClear /> </div> )}这时可以看到,首次进入页面,会发起 2 次查询全部的搜索数据请求,然后每次输入框更新,都会发起搜索数据的请求。
正常来说是不是我们在 onSearch 事件,加入 debounce,就能够做到当输入操作停顿指定时间后,才发起搜索数据的请求。
这里需要注意的是要加上 useCallback,来避免重复渲染导致的延时事件重复触发问题。
将 onSearch 方法用 debounce + useCallback 封装后,可以实现防抖效果。
const onSearch = useCallback( debounce((val) => { console.log('搜索', val || '全部') }, 500), [])接下来就是把防抖的逻辑按照 3 个不同的思路封装成不同的自定义 Hooks。
useDebounceFn
第一个自定义防抖 Hooks 的思路是,将上述 debounce + useCallback 封装为通用的自定义 Hooks useDebounceFn,实现一个有防抖效果的函数。
使用方式:useDebounceFn(fn1, options)。
interface DebounceOptions { wait?: number;}const useDebounceFn = (fn: (...args: any) => any, options: DebounceOptions) => { return useCallback(debounce(fn, options.wait), [])}const onSearch = useDebounceFn( (val) => { console.log('搜索', val || '全部') }, { wait: 500, })useDebounce
第二个自定义防抖 Hooks 的思路是创建一个新 state,setState 用 useDebounceFn 封装,返回一个具有防抖效果的 state。
使用方式:useDebounce(state, options)。
function useDebounce<T>(value: T, options: DebounceOptions) { const [debounced, setDebounced] = useState(value) const update = useDebounceFn((value) => { setDebounced(value) }, options) useEffect(() => { update(value) }, [value]) return debounced}将 useEffect 的依赖项改成 useDebounce 返回的 state,同样可以实现搜索防抖:
const debounceVal = useDebounce(val, { wait: 500 })const onSearch = (val: string) => { console.log('搜索', val || '全部')}// 当 debounceVal 发生变化时,请求搜索数据useEffect(() => { onSearch(debounceVal)}, [debounceVal])useDebounceEffect
第三个自定义防抖 Hooks 的思路是创建一个新 state,setState 用 useDebounceFn 封装,依赖更新时防抖更新 state,新 state 更新时执行副作用,这时副作用就防抖执行了,实现一个具有防抖效果的 useEffect。
使用方式:useDebounceEffect(effect, deps, options)。
function useDebounceEffect( effect: EffectCallback, deps: DependencyList, options: DebounceOptions) { const [debounced, setDebounced] = useState({}) const update = useDebounceFn(() => { setDebounced({}) }, options) useEffect(() => { update() }, deps) useEffect(effect, [debounced])}将 useEffect 改成 useDebounceEffect,就可以实现搜索防抖:
useDebounceEffect( () => { onSearch(val) } [val], { wait: 500 } )小结—
大家应该比较清晰自定义防抖 Hooks 的实现思路了,可以通过这个思路给自己组件内的通用逻辑封装成通用的 Hooks 啦。
本文实现的 useDebounceFn、useDebounce、useDebounceEffect 3 种防抖 Hooks,可以直接下载 ahooks[1] 使用。
参考资料—
[1]