react hooks节流需用useref存函数、usecallback稳引用、useeffect清定时器,确保节流函数调用最新状态且不触发重渲染或内存泄漏。

节流在 React Hooks 中结合 useRef 和 useCallback 实现,核心目标是:保持函数引用稳定、确保执行时拿到最新状态(不陷在闭包里)、避免定时器残留导致内存泄漏。关键不在“节流逻辑本身”,而在于如何让节流函数始终调用当前最新的业务逻辑和数据。
为什么必须用 useRef 存函数
React 函数组件每次渲染都会生成新函数,如果直接把 search 这样的回调传进节流逻辑,它会固化住那一帧的 inputValue 或其他 state —— 这就是闭包陷阱。用 useRef 把函数存起来,再通过 .current 去读取,就能保证每次执行时拿到的是最新版本。
-
ref 不随渲染重置:
fnRef.current = fn在每次渲染后更新,但 ref 对象本身不变 -
节流函数内部只调用
fnRef.current(),不依赖闭包捕获的旧值 - 避免 useEffect 无限循环:不用把 fn 当依赖项传进 useCallback,否则一改就重建节流函数
useCallback 确保节流函数引用稳定
节流函数本身要作为事件处理器(比如 onScroll 或 onChange)被反复传入,如果每次渲染都生成新函数,就会触发子组件重渲染或监听器重复绑定。用 useCallback 包一层,让它只在 delay 或底层逻辑真正变化时才更新。
Orderly React SDK 钩子使用参考指南,包括 useOrderEntry、usePositionStream、useOrderbookStream、useCollateral 等。
- 依赖数组写
[delay]就够了,fn交给 ref 管理 - 节流函数体里不直接写业务逻辑,而是调用
fnRef.current() - 这样既复用函数引用,又不牺牲状态新鲜度
完整节流 Hook 写法(带自动清理)
生产环境推荐带卸载清理的版本,防止组件销毁后定时器还在跑:
import { useCallback, useRef, useEffect } from 'react';
function useThrottle(fn, delay) {
const timerRef = useRef(null);
const fnRef = useRef(fn);
// 始终同步最新 fn,解决闭包问题
useEffect(() => {
fnRef.current = fn;
}, [fn]);
const throttled = useCallback((...args) => {
if (timerRef.current !== null) return;
timerRef.current = setTimeout(() => {
fnRef.current(...args);
timerRef.current = null;
}, delay);
}, [delay]);
// 组件卸载时清定时器
useEffect(() => {
return () => {
if (timerRef.current) {
clearTimeout(timerRef.current);
}
};
}, []);
return throttled;
}
用法示例:
const App = () => {
const [value, setValue] = useState('');
const handleSearch = useCallback(() => {
console.log('搜索关键词:', value); // 拿到的是实时 value
}, [value]);
const throttledSearch = useThrottle(handleSearch, 300);
return (
<input value="{value}" onchange="{e"> setValue(e.target.value)}
onKeyUp={throttledSearch}
/>
);
};
常见误区提醒
别把节流函数写成 useCallback(() => { ... }, [value]) —— 这样虽然能读到新 value,但每次 value 变,节流函数就重建一次,失去节流意义;也别用 useState 存节流函数,state 更新会触发额外渲染。真正高效的方式是:ref 管逻辑,callback 管引用,effect 管清理。










