React中直接取useRef(...).current赋值是否属于反模式?
对
const refName = useRef(...).current 写法的分析 这种写法唯一的动机就是少写几次.current,没有任何实际优势,反而存在致命的逻辑缺陷,绝大多数场景下都会出bug。
核心弊端
- 完全丢失ref的跨渲染同步能力
useRef的本质是返回一个组件全生命周期内引用地址固定的对象,所有可变值都存在这个对象的current属性上,React更新DOM、修改存值时只会修改这个固定对象的current字段。如果在渲染阶段直接把current取出来赋值给普通变量,相当于拿到的是当前渲染节点上current值的快照:如果是原始类型值就是值拷贝,如果是引用类型就是当时的引用地址,后续ref.current的任何更新都不会同步到你提前取出来的这个变量上,完全丧失ref跨渲染周期存值的核心作用。 - 根本无法正常更新ref存储的值
如果你用const声明这个取出来的变量,后续想修改值时会直接抛出赋值错误;就算改成let声明,修改的也只是当前渲染闭包里的局部变量,组件下一次重渲染时,这个变量会被重新赋值为初始的useRef(xxx).current,之前的修改直接丢失,根本达不到持久化存储的效果。 - 用来绑定DOM时100%拿不到目标节点
组件首次执行渲染函数时真实DOM还没挂载,此时useRef(null).current拿到的就是null。等React完成DOM挂载、给ref的current属性赋值真实节点时,你之前存到变量里的null不会有任何更新,后续所有DOM操作都会因为拿到空值失效。
典型错误场景复现:
function BadComponent() { // 首次渲染就取出current,存初始值 const timerRef = useRef(0).current; const domRef = useRef(null).current; useEffect(() => { // 直接报错:Assignment to constant variable timerRef = setInterval(() => console.log('tick'), 1000); // 永远输出null,拿不到button节点,因为传给ref属性的不是ref对象 console.log(domRef); return () => clearInterval(timerRef) }, []) return <button ref={domRef}>点我</button> }
唯一看似“能跑”的特殊场景
只有当你满足以下所有条件时,这种写法不会立刻出bug:
- 传入useRef的初始值是一个永远不需要修改的引用类型值(比如固定的工具函数、静态配置对象)
- 永远不会在后续逻辑中给这个ref的current属性重新赋值
但这种场景完全没必要用useRef,直接把常量定义在组件外部作为模块级变量即可,还能省去每次渲染调用useRef的开销。
如果真的觉得反复写.current麻烦,也不要用这种取巧的错误写法,老老实实使用标准的ref对象即可——.current本身就是明确的语义标记,提醒你这是一个不会触发重渲染的可变跨渲染值,反而能降低代码维护时的认知成本。
内容的提问来源于stack exchange,提问作者Grover
相关产品推荐
相关产品推荐

