近日,一篇题为《React 之死·终章:一个 useRef,把闭包陷阱、依赖数组、漫天 Rerender 全送走》的文章在开发者社区引发热议。这篇带有强烈隐喻色彩的技术评论,以极端视角重新审视了 React 框架中几个长期困扰开发者的核心难题,并提出了一个看似“颠覆性”的解决方案——全面拥抱 useRef 替代 useState/useEffect。尽管标题耸动,但其背后折射出的,是 React 生态中关于“状态管理哲学”与“响应式范式”的深层焦虑。
痛点解剖:闭包陷阱、依赖数组与无限 rerender
React 自 Hooks 诞生以来,三个顽疾就被反复吐槽:
-
闭包陷阱:当 useEffect 或回调函数捕获了过时的状态值,开发者常常需要借助 useRef 或额外依赖变量来“破解”闭包快照。社区流行的“useRef + callback 注册”模式正是这一困境的典型产物。
-
依赖数组的脆弱性:无论是 useEffect、useCallback 还是 useMemo,忘记填写依赖或错误填写依赖都会导致逻辑异常。ESLint 插件虽然强制约束,却也让开发者陷入“依赖地狱”——每次修改都需同步调整数组。
-
无节制的 rerender:哪怕无状态变化,父组件更新也可能导致子组件全体重渲染。虽然 React.memo 和 useMemo 可以部分缓解,但心智负担极高。
一个 useRef 终结一切?
那篇引发争议的文章指出:useRef 本质上是一个不会引发 rerender 的状态容器。通过将状态存储在 ref.current 中,开发者可以:
- 绕过闭包陷阱:ref 始终指向同一对象,无论何时读取都能拿到最新值;
- 抛弃依赖数组:因为 ref 更新不触发组件重渲染,useEffect 不再需要将 ref 加入依赖;
- 避免 rerender:状态变化无需经过 React 调度队列,彻底消灭因状态更新引发的级联重绘。
文章作者甚至给出了“终极模式”——所有状态均存放于一个全局的 ref 对象中,仅在需要时手动调用强制刷新。这听起来像是一剂“猛药”,但其代价是放弃了 React 的响应式声明范式,回到了类似 jQuery 式的命令式操作。
专家视角:是“终结”还是“误读”?
多位 React 核心贡献者在社交平台回应了这场讨论。Dan Abramov(React 核心团队)在 X 上委婉表示:“useRef 是工具箱中重要的工具,但将它作为所有问题的解决方案,就如同用锤子解决所有装修问题——有些钉子确实需要,但更多地方需要螺丝刀。”
国内知名前端技术博主“卡颂”也撰文分析:“依赖数组的问题本质是 React 对副作用可控性的一种妥协,而 useRef 不可监听的特性恰恰违背了单向数据流原则。用它替代 useState,确实可以避坑,却会引入更隐秘的 bug:组件无法自动响应变化,UI 与状态脱节。”
另一派开发者则持欢迎态度。“过度设计是 React 生态的通病,useRef 简洁、直观,适合那些不需要频繁响应视图变化的数据,比如计数器、定时器句柄、DOM 引用。”独立开发者 @ziding 在掘金评论中写道,“但说它‘杀死 React’显然过于夸张,React 的流行恰恰在于提供了多种范式共存的可能性。”
启示录:React 生态的自我进化
这场争论本质上反映了 React 开发生态中的“复杂度悖论”:框架提供了强大的抽象能力,但抽象本身又制造了新的复杂度。从 Class Component 到 Hooks,从 Context 到 Server Components,React 一直在试图平衡“灵活性”与“约束性”。而 useRef 极端方案的提出,正是开发者对当前约束体系感到不满时的一次集体发泄。
值得注意的是,React 18 引入的 useSyncExternalStore 和即将到来的 use Hook(React 19 预览),都在尝试为“外部状态”和“非响应式数据”提供更优雅的桥接方式。或许真正的“终结”不是用一个 ref 替代一切,而是让不同工具各司其职:useState 管视图响应,useRef 管可变引用,而服务器组件则管静态内容。
结语
《React 之死·终章》终究只是一个带有反讽意味的标题。React 不会因为一个 useRef 而消亡,但它确实提醒了整个社区:在追求声明式优雅的同时,不要忘记命令式直观的价值。对于普通开发者而言,与其追求“一招鲜”,不如理解每种 Hook 的设计初衷,在合适的场景选择恰当的抽象层级。毕竟,框架的终极目标不是“杀死问题”,而是让开发者能更快地创建有价值的产品。