React 17 useEffect清理机制中可变ref值的潜在问题与相关疑问
关于React 17 ref相关问题的解答
1. 为什么ref.current会在组件卸载到清理函数执行的间隔被置为null?
React 17对effect清理机制做了性能优化:组件卸载时的useEffect清理逻辑从同步执行改为异步延迟执行,会等到屏幕更新渲染完成后再运行,目的是避免大量卸载清理任务阻塞页面视觉更新,提升切换路由、卸载大组件场景下的响应速度。
这个改动就产生了「组件卸载完成」到「useEffect清理函数执行」之间的时间窗口,ref.current的修改就可能发生在这个窗口里。
2. 是谁把ref.current置为null的?
两种情况都存在:
- 绑定到DOM元素的ref:由React内部自动处理,卸载对应DOM节点的同步流程里,React会直接把关联的
ref.current置为null - 普通自定义ref(比如你用来存回调函数的ref):是用户自己的代码逻辑修改的,比如在同步执行的卸载流程里手动给
ref.current赋值了null
3. 用户怎么在这个间隔修改ref.current?为什么React 17之前不存在这个问题?
用户修改的实现方式
组件卸载的同步流程(包括useLayoutEffect的清理函数、类组件的componentWillUnmount生命周期)的执行优先级远高于异步延迟的useEffect清理,如果你在这些同步卸载逻辑里修改了ref.current的值,后续执行的useEffect清理函数拿到的就是修改后的值。
React 17之前不存在该问题的原因
React 16及更早版本中,所有effect清理逻辑都是同步执行的:组件卸载时会连续跑完所有卸载相关的逻辑(从DOM卸载、生命周期/useLayoutEffect清理到useEffect清理),中间没有插入其他任务的间隙,ref.current就算被修改也会立即被清理函数读取,不会出现时间差导致的取值不一致问题。
补充说明(针对你的自定义hook场景)
你实现useMountEffect时用的普通自定义ref,只要你没有在同步卸载流程里手动把funcRef.current置为null,就不会触发该问题,你的实现逻辑是成立的。
内容的提问来源于stack exchange,提问作者Ashish
相关产品推荐
相关产品推荐

