如何在React的resize事件中复用现有useDebounce钩子?
复用useDebounce钩子适配Resize事件的两种方案
原实现代码
现有的useDebounce钩子
import { useEffect, useState } from 'react'; export default function useDebounce(text: string, delay: number) { const [value, setValue] = useState(''); useEffect(() => { const timerId = setTimeout(() => { setValue(text); }, delay); return () => { clearTimeout(timerId); }; }, [text, delay]); return value; }
当前的Resize事件绑定代码
useEffect(() => { const handler = () => { if (liRef.current) setWidth(liRef.current.clientWidth); }; window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []);
方案1:不修改原钩子,通过中间状态适配
思路:用一个状态存储实时获取的宽度,将该状态传入原useDebounce得到防抖后的值,再监听防抖值更新最终状态。
步骤1:优化原钩子为泛型(可选,支持非字符串类型)
如果需要让原钩子支持数字等其他类型,修改为泛型版本:
import { useEffect, useState } from 'react'; export default function useDebounce<T>(value: T, delay: number) { const [debouncedValue, setDebouncedValue] = useState<T>(value); useEffect(() => { const timerId = setTimeout(() => { setDebouncedValue(value); }, delay); return () => clearTimeout(timerId); }, [value, delay]); return debouncedValue; }
步骤2:组件中使用
import useDebounce from './useDebounce'; import { useEffect, useState, useRef } from 'react'; function YourComponent() { const liRef = useRef<HTMLLIElement>(null); const [width, setWidth] = useState(0); // 存储实时宽度的中间状态 const [rawWidth, setRawWidth] = useState(0); // 传入原useDebounce获取防抖后的值 const debouncedWidth = useDebounce(rawWidth, 300); // 监听防抖值变化,更新最终状态 useEffect(() => { setWidth(debouncedWidth); }, [debouncedWidth]); // 绑定Resize事件,更新实时宽度 useEffect(() => { const handler = () => { if (liRef.current) { setRawWidth(liRef.current.clientWidth); } }; window.addEventListener('resize', handler); return () => window.removeEventListener('resize', handler); }, []); return <li ref={liRef}>宽度:{width}px</li>; }
方案2:基于原逻辑扩展防抖回调钩子
如果不想维护额外中间状态,可以基于原useDebounce的定时器逻辑,封装一个专门处理函数防抖的钩子。
扩展useDebounceCallback钩子
import { useEffect, useRef } from 'react'; // 基于原防抖逻辑封装的回调防抖钩子 export function useDebounceCallback<T extends (...args: any[]) => void>(callback: T, delay: number) { const callbackRef = useRef<T>(callback); // 更新回调引用,避免闭包问题 useEffect(() => { callbackRef.current = callback; }, [callback]); return (...args: Parameters<T>) => { const timerId = setTimeout(() => { callbackRef.current(...args); }, delay); return () => clearTimeout(timerId); }; }
组件中使用
import { useDebounceCallback } from './useDebounceCallback'; import { useEffect, useState, useRef } from 'react'; function YourComponent() { const liRef = useRef<HTMLLIElement>(null); const [width, setWidth] = useState(0); // 创建防抖后的Resize处理函数 const debouncedResizeHandler = useDebounceCallback(() => { if (liRef.current) { setWidth(liRef.current.clientWidth); } }, 300); // 绑定Resize事件 useEffect(() => { window.addEventListener('resize', debouncedResizeHandler); return () => window.removeEventListener('resize', debouncedResizeHandler); }, [debouncedResizeHandler]); return <li ref={liRef}>宽度:{width}px</li>; }
内容的提问来源于stack exchange,提问作者pvp11
相关产品推荐
相关产品推荐

