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

React中useRef渲染流程疑问:多次点击Show按钮后的日志解析

问题分析与解答

测试代码

function Page() {
  const ref = useRef(null)
  const [show, setShow] = useState(false);
  const [refHasValue, setRefHasValue] = useState(false);

  console.log(ref, show, refHasValue);

  useEffect(() => {
    if (ref.current) {
      setRefHasValue(true); 
    }
  }, [ref.current]);

  return (
    <>
      <button onClick={() => setShow(true)}>Show</button>      
      {show && <div ref={ref}>Hello</div>}
      {refHasValue && <p>Ref has value</p>}
    </>
  );
}

控制台日志

  • {current: null} false false(初始渲染)
  • {current: null} true false(首次点击Show按钮后)
  • {current: div} true false(再次点击Show按钮后)

1. 为什么第三次会触发渲染?

第二次点击Show按钮时调用了setShow(true),尽管当前show状态已经是true,但对于未用React.memo包裹的函数组件来说,只要调用setState,无论新值与旧值是否相等,都会触发一次重渲染,这就是第三次渲染的直接原因。

2. 为什么useEffect监听ref.current却没更新refHasValue?

核心在于React渲染流程与ref、useEffect的执行时机:

  • 首次点击Show后,show变为true,组件进入渲染阶段:此时<div>被纳入虚拟DOM,但真实DOM尚未挂载,ref.current仍为null。渲染完成后进入commit阶段,React才会挂载真实DOM并将ref.current设置为对应的div元素。但ref是可变对象,它的变化不会触发React重渲染,因此此时不会重新执行useEffect(渲染阶段捕获的依赖是null,和初始渲染的依赖一致)。
  • 第二次点击触发重渲染时,真实DOM已经存在,ref.current在渲染阶段就已经是div元素了。此时useEffect的依赖ref.current从之前的null变为div对象,依赖发生变化,useEffect会在这次渲染完成后执行,调用setRefHasValue(true)进而触发第四次渲染(日志会显示{current: div} true true)。你看到的第三次日志只是渲染阶段的打印,此时useEffect还未执行,所以refHasValue仍然是false。

另外需要注意:useRef的current属性变化不会触发组件重渲染,这也是首次点击后ref.current变为div却没有自动触发新渲染的原因。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 14:05:11