You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React组件注册事件监听器时,useRef替代useState的原因咨询

关于React中useRef与useState在事件监听回调中的差异问题

问题代码

const movingRef = useRef(false);

const handleStartMove = (e) => {
  movingRef.current = true;

  e.stopPropagation();
  // handleMove doesn't take the latest value if we use a state instead of a ref
  document.addEventListener('mousemove', handleMove);
  if (onMoveStart) {
    onMoveStart(e, movingRef.current, angle);
  }

};

const handleMove = (e) => {
  e.stopPropagation();
  e.preventDefault();

  if (!movingRef.current) {
    return;
  }
  this.moveDOMElement(e);
};

问题描述

将上述代码中的movingRef = useRef(false)替换为const [moving, setMoving] = useState(false)后,代码无法正常运行;而使用useRef则可正常工作。我了解到useRef的变更传播更快,因为回调函数handleMove会获取useState变量moving的缓存值而非最新值,而useRef总能获取最新值。请问情况是否如此?

我听说这是闭包相关的行为,使用state变量会引发同步和副作用问题,而useRef可避免此类问题,达到预期效果。


回答

你的理解核心是对的,但“变更传播更快”这个说法不准确,本质原因是闭包捕获变量的特性:

  • 用useState定义moving时,组件每次重新渲染都会生成新的moving变量和新的handleMove函数。但handleStartMove中添加的mousemove监听,绑定的是当前渲染周期的handleMove——这个函数捕获的是当前周期的moving值(初始为false)。后续调用setMoving(true)会触发组件重新渲染,生成新的moving和新的handleMove,但旧的handleMove依然被mousemove监听引用着,它里面的moving永远是最初的false,导致handleMove里的判断永远不通过,逻辑失效。

  • 而useRef的current属性是一个可变的容器,它不会随着组件重新渲染而创建新的变量,所有渲染周期的函数都共享同一个current引用。不管handleMove是哪个渲染周期创建的,它访问的都是同一个movingRef.current,总能拿到最新的值,所以逻辑可以正常执行。

总结来说:useState的变量是“快照式”的,每个渲染周期的变量都是独立的;useRef的current是“引用式”的,始终指向同一个内存地址,能获取最新值。


内容的提问来源于stack exchange,提问作者user11092881

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 06:37:13