关于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} />;
}
posted @   IslandZzzz  阅读(129)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示