关于useEvent以及它解决了什么问题
useCallback闭包与setState更新矛盾
useCallback的函数fn,使用的state, 是闭包中的state
如果每次render要拿到最新的stata, 就要将state放在依赖数组上,否则读取的总是闭包中的state, 而不是最新的state
但是这样一来, 就因为新增了useCallback的依赖,失去了useCallback缓存fn,保持fn引用不变的初衷
export default function App() {
const [counter, setCounter] = useState(0);
const handleClick = useCallback(() => {
console.log(counter);
setCounter(counter => counter + 1);
}, []);
return (
<div onClick={handleClick}>
click to add counter
counter: {counter}
</div>
)
}
因此引入useEvent, 主要基于ref+useCallback实现useEvent来解决这个问题
// (!) 近似行为
function useEvent(handler) {
const handlerRef = useRef(null);
// 每次render都在ref中维持一个最新的handler引用,从而拿到最新的上下文环境以及状态。在实际实现时它会在 layout effect 之前执行
useLayoutEffect(()=>{
handlerRef.current = handler;
})
// 返回一个useCallback包裹的依赖数组长度为0的函数,在useCallback的缓存函数里调用handler
// 使得handler执行时是最新的上下文环境与状态,绕过了闭包陷阱,去掉了多余的deps,同时保证了onClick引用不变
return useCallback((...args) => {
const fn = handlerRef.current;
return fn(...args);
}, []);
}
function Chat() {
const [text, setText] = useState('');
// ✅ Always the same function (even if `text` changes)
const onClick = useEvent(() => {
sendMessage(text);
});
return <SendButton onClick={onClick} />;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具