useMemo与useEffect在Ref依赖下的行为差异探究
为什么useMemo依赖ref时无法捕获ref.current的更新?
核心原因很直白:useRef返回的是同一个引用对象,useMemo和useEffect的依赖数组对比的是「引用是否变化」,而非ref.current的值。结合你的代码和React的渲染流程,具体拆解如下:
关键前提先理清
- useRef创建的对象,在组件整个生命周期里引用完全不变——不管ref.current怎么改,这个对象本身的内存地址始终没变化。
- React的渲染流程是:渲染阶段(执行组件函数、计算JSX)→ 提交阶段(更新DOM、给ref.current赋值)→ 执行useEffect这类副作用。
你的组件实际运行流程(开发环境Strict Mode下)
你能看到两次渲染日志,是因为React Strict Mode在开发环境会刻意重复渲染,用来检测不安全的副作用:
第一次渲染
- 执行组件函数,调用
useRef()得到一个{current: undefined}的对象,就叫它refObj。 - 执行useMemo:依赖数组是
[refObj],此时ref.current还是undefined,所以输出memo ref undefined。 - 渲染阶段结束,进入提交阶段:React把
<div>元素赋值给refObj.current。 - 执行useEffect:此时ref.current已经是DOM元素,输出
effect ref {current: div}。 - Strict Mode触发effect清理,然后重新触发组件渲染(第二次渲染)。
第二次渲染
- 再次执行组件函数,调用
useRef()返回的还是之前的refObj(引用完全一致)。 - 执行useMemo:依赖数组
[refObj]和上一次没变化,哪怕Strict Mode下会重新执行useMemo,此时还处于渲染阶段,ref.current还没被重新赋值(提交阶段才会处理ref),所以依然输出memo ref undefined。 - 渲染阶段结束,提交阶段再次把
<div>赋值给refObj.current。 - 执行useEffect:再次输出
effect ref {current: div}。
为什么useEffect能拿到最新的ref.current?
useEffect是在提交阶段之后才执行的,这时候React已经完成了DOM更新和ref赋值,所以每次effect执行都能拿到最新的ref.current值。哪怕依赖数组里的ref引用没变化,Strict Mode下也会重复执行effect,所以你看到两次div的日志。
为什么useMemo拿不到?
useMemo是在渲染阶段执行的,这时候ref.current还没被赋值(要等提交阶段才会处理)。而且因为ref的引用自始至终没变化,依赖数组不会触发useMemo重新计算——就算Strict Mode下重复渲染,useMemo执行时ref.current依然是undefined。
简单总结:
- useMemo在渲染时运行,此时ref还未绑定DOM;加上ref引用不变,不会触发重新计算。
- useEffect在渲染完成后运行,此时ref已经绑定DOM,自然能拿到最新值。
内容的提问来源于stack exchange,提问作者machineghost
相关产品推荐
相关产品推荐

