普通视图

发现新文章,点击刷新页面。
今天 — 2026年3月8日首页

React 正在演变为一场不可逆的赛博瘟疫:AI 投毒、编译器迷信与装死的官方

作者 寅时码
2026年3月8日 10:59

React 正在演变为一场不可逆的赛博瘟疫:AI 投毒、编译器迷信与装死的官方

React 正在沦为前端圈的“孔乙己”:脱不下的长衫与失控的基建

React 团队一到前端圈,所有敲代码的人便都看着他笑,有的叫道:“React,你又悄摸摸搞出几个极其拧巴的缝合怪 API!”

他不回答,对柜里说:“加两套重型编译器,要一碟 useEffectEvent。”便排出几个难懂的心智模型。

他们又故意的高声嚷道:“你一定又在底层偷偷搞副作用(Side Effects)了!”

React 睁大眼睛说:“你怎么这样凭空污人清白……”

“什么清白?我前天亲眼见你的 useEffect 里异步网络请求满天飞,闭包陷阱套着闭包陷阱,连个最新状态都拿不到,还在 commit 阶段偷偷改 Ref,被全网吊着打。”

React 便涨红了脸,额上的青筋条条绽出,争辩道:“异步……异步的事怎么能叫副作用呢!……那叫代数效应(Algebraic Effects)!React 调度器里的事,能算不纯么?”接连便是些难懂的话,什么“Fiber 架构”,什么“并发渲染(Concurrent)”,什么“UI 是状态的纯函数映射”,引得整个 Web 社区内外充满了快活的空气。


你明明一身历史包袱,底层 DOM 突变和时序补丁糊了一层又一层,还要死死捂着那件打满补丁的「函数式编程」长衫装清高。你连业务代码里最基本的异步抓取和状态流转都做不到开箱即用,还天天搁这儿给开发者念经,说什么“要保持纯洁,要无副作用”。

既然你端着全球最大前端基建的架子,那我就得用配得上你这份傲慢的严苛标准来伺候你。我不听你那套自欺欺人的八股文,也不陪你玩“心智模型”的文字游戏。我只负责把你剥个精光,拿着放大镜逐行扒开你 ReactFiberCommitWork.js 的源码,把你装死关掉的 Issue 挨个掘出来。


一、useEffectEvent:设计拧巴,文档更拧巴

1.1 经典恶心场景:闭包逼你把「只想读一次」的东西写进依赖

所有写过 React 的人都遇到过这种事:

你写了一个聊天室组件,连接成功后要弹个提示,提示要用当前的主题色 theme

function ChatRoom({ roomId, theme }) {
  useEffect(() => {
    const connection = createConnection(roomId)
    connection.on('connected', () => {
      showToast('连接成功!', theme) // 这里用到了 theme
    })
    connection.connect()

    return connection.disconnect
    // ...
  }, [roomId, theme]) // 🚨 噩梦来了:React 逼你把 theme 加进依赖数组
}

问题在哪?

你只是想在弹窗时读取一下最新的颜色(theme),但因为 React 的闭包机制,你被迫把 theme 写进依赖数组。
结果就是:用户随便切个暗黑模式(theme 变了),你的聊天室就会断开重连一次。 这简直是灾难。

1.2 官方的解法:useEffectEvent

React 在 19.2 给出了 useEffectEvent:把「需要最新值、但不想加依赖」的逻辑包起来。

function ChatRoom({ roomId, theme }) {
  const onConnected = useEffectEvent(() => {
    showToast('连接成功!', theme) // 这里总能拿到最新的 theme
  })

  useEffect(() => {
    const connection = createConnection(roomId)
    connection.on('connected', () => {
      onConnected()
    })
    connection.connect()
  }, [roomId]) // 🎉 roomId 变了才重连,theme 终于不用加进依赖了
}

听起来是个好东西。

1.3 槽点:极度反人类的调用限制

React 给这个 API 加了一条硬性限制

"A function wrapped in useEffectEvent can't be called during rendering."

也就是说:这个函数绝对不准在组件渲染过程中调用,只能在 Effect 或者事件里调用。
如果你把它传给子组件,子组件在 render 里 调了一下,整个应用直接报错白屏

为什么限制这么死?

源码里,这个函数的最新值是在 DOM 渲染完之后的 commit 阶段 才挂到内部 ref 上的(见 ReactFiberCommitWork.js)。渲染的时候它还是个空壳或旧值。官方解决不了这时序问题,于是粗暴地用报错来阻止开发者

相比之下社区是怎么做的?

社区早就用 useRef 自己封了一个 useLatestCallback(或等价物):包装函数引用稳定,调用时总是执行当前 ref 里的最新函数。
没有任何调用时机的限制,想在哪调在哪调——render、effect、事件、传给子组件,都不会因为「during rendering」被拦。

详见我的 useLatestCallback 实现:github.com/beixiyo/rea…

官方无视社区极其好用的作业不抄,偏要自己造一个内部实现极其拧巴、强加各种规则、心智负担极重的 useEffectEvent。这就是设计拧巴,文档更拧巴

引用:


二、赌徒的执念:从 Prepack 到 Compiler,用魔法掩盖缺陷

2.1 Prepack:React 团队的「前科」

一句话:Prepack 是 Meta(Facebook)在 2017 年左右搞的一个失败的 JavaScript 编译器项目

当时 Facebook 异想天开:既然 JS 运行慢,能不能在打包编译的时候就把能算出来的代码提前算好?

比如 let a = 1 + 2 在编译时直接变成 let a = 3。后来他们试图把 Prepack 用在 React 上,想在编译期把组件提前「折叠」优化。

但 JavaScript 太动态了,这个饼根本画不圆,项目黄了,被官方放弃

为什么文章里要提它?

因为 React 团队对「编译器」有一种病态的执念
当社区都在拥抱 Signal(用运行时的细粒度响应式解决性能问题)时,React 偏不。他们觉得:当年 Prepack 虽然失败了,但我现在搞个缩小版叫 React Compiler,继续搞编译期魔法。
提 Prepack 就是为了扒皮:React 宁可在「编译期优化」这条曾走过弯路的老树上吊死,也不肯听社区的意见去换一套更先进的响应式模型(Signal)。

2.2 不承认模型问题,用编译器堆屎山

面对依赖数组带来的灾难,社区呼吁引入 Signal 这种现代化的细粒度响应式。React 选了啥?选了他们当年失败过的「编译期优化」老路。

从当年胎死腹中的 Prepack,到如今的 React Compiler,官方展现出一种惊人的技术执念
我们宁可造一个巨型编译器来强行分析依赖、强行插入缓存,也绝不承认「组件级渲染 + 依赖数组」这个底层模型本身已经落后了。

这不叫优雅的工程演进,这叫为了掩盖第一代屎山的恶臭,强行喷香水

  • React Compiler 1.0(2025-10)称:"automatically optimizes components and hooks without requiring rewrites"。也就是:靠编译期自动优化兜住现有模型,而不是改模型。
  • React Labs 的 Automatic Effect Dependencies:嘴上说「effect 难理解」,手上在「依赖数组 + 组件树」上继续叠编译器与 IDE,而不是提供更简单的抽象(Signal 或至少 useLatestCallback 这类通用稳定回调)。

引用:


三、Signal:社区要,官方不接,关 Issue 不解释

3.1 社区直接问:为什么不做 Signal?

GitHub #27164"feature: make react more reactive (feedback for future)"):

  • 作者提出:useState/useEffect 本质是 observable + subscriber,但「读到的不是最新值」「依赖数组难写」,"Just implement signals. It will reduce complexity."
  • 结果没有任何 React 官方成员回复。Issue 被 stale bot 自动关掉(Resolution: Stale, closed as not_planned)。
    社区认真提的「换一种更简单的模型」被冷处理,连一句「我们考虑过,因为 XXX 所以选 Compiler」都没有。

GitHub #31393"React Why Not Consider Support Signals"):

  • 作者问:为什么官方不考虑 Signal 这种显而易见的方案?
  • 结果Joseph Savona(React Compiler 负责人)关闭,唯一一句回复
    "We covered this pretty thoroughly in our React Conf talk about performance." 附了一个 React Conf 演讲链接。
    也就是说:没有在 issue 里写任何「为什么不支持 Signal」「技术选型理由」,而是把问题推到「我们在一场演讲里讲过」——不写进文档、不写进 RFC、不留在 issue 里,等于让后来者自己去找视频听,且无法被搜索和引用。

3.2 这算啥?

  • #27164:零官方回复,stale 关掉 → 不接话、不解释
  • #31393:仅回复「Conf 里讲过」,不给正文、不给摘要 → 死鸭子嘴硬:既不承认「我们就是选 Compiler 不选 Signal」,也不在公开文本里说明选型理由。

身为被全世界当基建的库,对「为什么不做 Signal」这种级别的讨论,不在 issue / 博客 / RFC 里留下可检索的、负责任的说明,而是用「去听我们某次 Conf」打发,这是对社区反馈的轻慢,也是技术傲慢

替代品:满分作业拍在脸上,React 偏不抄

其实证明 React 底层不仅能上 Signal、而且能上得极其优雅的铁证,早就摆在那里了。

社区掏出的 Preact Signals,直接把运行时细粒度更新的满分作业拍在了官方脸上。它完美兼容现有的组件模型,直接证明了闭包陷阱完全可以靠一套现代化的响应式机制来根治。

最打脸的是什么?人家一个第三方库,在根本碰不到你 React 核心源码的情况下,都能靠外挂把这套机制跑得明明白白。而你官方握着 Fiber 调度器的生杀大权,却选择视而不见,死活说“做不了”。

只能说 React 团队这几年确实是写编译器写魔怔了。简单的运行时解法他们不屑于做,正路不走,非得拉坨大的出来,好像不搞个重型编译链就配不上大厂的 KPI 一样。

更可笑的是,如果你现在想用 Preact Signals,你会发现它跟官方硬推的 React Compiler 是直接冲突的。

为什么冲突?因为 React Compiler 根本就不是什么优雅的架构演进,它本质上就是一个极其自负的 AST 爆改插件。你原本干干净净的代码,被它过一遍,AST 树上全是被它强行塞进去的 useMemo 和缓存标记,代码执行轨迹完全成了一个黑盒。

搞得这么抽象,不知道的还以为你搁这做 JIT 呢。

(如果你也受够了官方这种强行喂屎的黑盒操作,想看看怎么在 React 屎山里自救,详见我踩坑两年写出的血泪总结:《花了两年用遍了 React 所有状态管理库,我选出了最现代化的 Signal 方案》)

引用:


四、技术选型:Compiler 而非 Signal,且不写清楚

从公开信息能拼出的「为什么是 Compiler 而不是 Signal」大致是:

  1. 架构和历史包袱:React 的调度、并发、SSR、reconciler 都是按「组件树 + 依赖数组」建的。原生 Signal 是细粒度订阅,要接进去等于在核心里再塞一套响应式模型,改动面巨大。
  2. 已经押注 Compiler:从 Prepack 到 React Compiler,团队长期押「用编译期优化」来逼近「少重渲染、少依赖心智」,而不是在 runtime 换一套响应式。公开承认「该上 Signal」等于承认这条路线不够,所以不会在官方叙事里这么说
  3. 生态与兼容:全世界都是 setState + deps,真要内置 Signal 要么长期双轨,要么 breaking 大改,政治和生态成本都高。

但这些没有在任何官方博客、RFC 或上述 issue 里被系统写出来
选型结果就是:Compiler + 更多工具链;对 Signal 的态度是:不计划、不接题、关 issue 时指到 Conf 视频
技术选型存在,但解释不透明;社区问「为啥不 Signal」得不到可检索的、负责任的答复——这就是技术傲慢:我们怎么做你们就怎么用,理由你们自己找。


五、为什么非要逮着 React 骂?

我可以不用 React,但躲不开

总有人问我:“既然你这么懂,自己封装一套解法不就行了?干嘛天天逮着骂?”

说实话,React 这一地鸡毛的闭包陷阱、渲染地狱,我早就摸透了,甚至有极其成熟的解法和替代方案。但这不代表我觉得它合理!这种开发模式简直蠢透了! 我一个做业务开发的,凭什么要天天搁这儿给框架擦屁股?

5.1 恶臭的鄙视链与叹息之墙:毒害行业新人

前端圈一直有股令人作呕的风气:“React 孝子”们看不起 Vue 等其它框架。他们把「用 React」当成政治正确,把对 React 的批评当成异端。结果就是烂设计没人敢往死里骂,屎山越堆越高。

它不仅折磨老手,更是在新人面前砌起了一堵叹息之墙。新人满怀热情想画个交互,结果光是搞懂 useEffect 为什么会死循环、定时器里的 State 为什么永远停留在上个世纪,就得先脱两层皮。 硬生生把一个前端门槛搞得如此畸形、反直觉,官方非但不反思,那一群孝子反而把这种极高的心智负担当成“技术深度”四处炫耀。把喂屎包装成“最佳实践”,这就是你们引以为傲的工程化?

5.2 AI 投毒:机传人的赛博瘟疫

进入 AI 时代,这场灾难彻底演变成了赛博瘟疫。全网投喂的语料导致现在的 AI 写前端默认就是 React,十有八九是 JSX + hooks。

我现在日常开发主要靠 AI 写代码,但面对 React 这个奇葩,即便我在 Prompt 和工作流里写了上百行的防坑铁律严防死守,AI 还是会时不时被 React 那套反人类的阴间规则绕晕,悄无声息地给你拉一坨极其隐蔽的屎。 到头来,我还得停下手中的活,亲自下场 Debug,拨开那一层层令人窒息的依赖数组,去查看到底是底层哪个 Hook 又在发癫。这不是在写代码,这是在做赛博排雷。整个 Web 社区的代码基建正在不可逆转地走向失控的屎山化。

5.3 SDK 绑架:强买强卖的生态流氓

你以为你不用 React 就能独善其身?直到有一天,你接一个核心的 SDK,点开文档一看:对不起,只有 React 版本。 为了用这一个组件,你被迫在项目里引入整套 React 运行时,被迫去吃那一套恶心的 Hooks 闭包。这不叫技术选型,这叫强买强卖的生态流氓。

所以,你问我为什么逮着 React 骂? 因为他早已不是一个你可以躲开的工具,而是一场避无可避的生态瘟疫。骂 React,不是在骂「一个你可以不用的库」,而是在骂已经失控的基建


狂热粉丝只会告诉你 useEffectEvent 怎么用,而我会翻出 ReactFiberCommitWork.js 的源码告诉你它为什么这么难用; 小白面对被关掉的 Issue 只会觉得是自己提错了,而我会顺着线索找到那场企图搪塞一切的 React Conf 视频。

我拿着所有的官方日志、源码和 Issue 链接站在这里,指着这些拧巴的设计说:作为基建,你现在的傲慢、闭门造车和对社区声音的冷处理,真的很难看。

这不是毫无逻辑的狂喷,而是学霸拿着满分试卷在教训连及格线都没达到的出题人—— 用详实的论据、严密的逻辑和底层的代码把你锤得体无完肤。


引用链接汇总

类型 内容 链接
官方博客 React 19.2 发布(Activity, useEffectEvent, cacheSignal 等) react.dev/blog/2025/1…
官方博客 React Compiler 1.0 react.dev/blog/2025/1…
官方博客 React Labs: View Transitions, Activity, Automatic Effect Dependencies react.dev/blog/2025/0…
官方文档 useEffectEvent Reference react.dev/reference/r…
GitHub #27164 – make react more reactive / implement signals(无官方回复,stale 关闭) github.com/facebook/re…
GitHub #31393 – Why Not Consider Support Signals(Joseph Savona 回复 Conf 链接后关闭) github.com/facebook/re…
源码 ReactFiberHooks.js(useEffectEventImpl, 渲染期禁止调用) github.com/facebook/re…
源码 ReactFiberCommitWork.js(commit 阶段更新 effect event ref.impl) github.com/facebook/re…
官方 Joseph Savona 在 #31393 中指向的 React Conf performance 演讲 www.youtube.com/watch?v=zyV…
❌
❌