网易首页 > 网易号 > 正文 申请入驻

React团队藏了3年的信号方案,被这7行代码捅破了

0
分享至


React 19还没捂热,社区里一组实验代码的Star数已经悄悄破了4000。不是新框架,是有人把Signals塞进了React——而且没动一行React源码。

这事有意思的地方在于:React团队自己也在搞Signals(就是那个叫use的Hook),但进度慢得像在挤牙膏。社区等不及了,直接上手造了个能跑的生产级方案。今天这篇就是作者系列的第四篇,讲清楚一个关键问题——数据副作用和UI副作用,到底该谁管?

7行代码,拆出两条完全不同的生命周期

先看这段被转发最多的代码:

// data/heartbeat.ts import { signal } from "../core/signal"; import { createEffect, onCleanup } from "../core/effect"; export const intervalMs = signal(1000); export const heartbeat = signal(null); createEffect(() => { const ms = intervalMs.get(); const id = setInterval(() => { heartbeat.set(new Date()); }, ms); onCleanup(() => clearInterval(id)); });

7行核心逻辑,干了一件React里很别扭的事:让一个定时器跟着数据走,而不是跟着组件走。

作者管这叫"数据层的心跳"——intervalMs是个信号,改它的时候,旧的定时器自动清理,新的自动启动。整个过程没有组件参与,页面切走了它还在跑,页面切回来数据还是热的。

对比React原生的写法,差别立刻显现。以前你要么把定时器塞useEffect里跟着组件生死,要么上Redux-Saga、React Query这种重型方案。现在7行代码搞定,而且类型安全。

光标闪烁:为什么必须用React的useEffect?

作者紧接着抛了另一个例子,刻意和上面的形成对照:

// ui/Blinker.tsx export function Blinker({ enabled = true }) { const [on, setOn] = useState(false); useEffect(() => { if (!enabled) return; const id = setInterval(() => setOn(v => !v), 500); return () => clearInterval(id); }, [enabled]); return |; }

同样是定时器,这次老老实实用了React的useEffect。为什么?


因为光标闪烁是纯视觉行为,它依赖React的渲染周期——enabled prop变了要立刻停,组件卸载要立刻清。这些时机必须对齐React的commit阶段,而不是数据的任意变更。

作者的原话很直接:「这是纯粹的UI/视觉行为,它的清理时机应该跟随React的提交周期。」

两个例子摆在一起,分界线就清楚了:createEffect管数据流的生命周期,useEffect管DOM的生命周期。以前这两件事被混在一个Hook里,现在物理隔离。

Dashboard组件:两条河怎么汇到一处

真正用起来的时候,开发者面对的其实是混合场景。看作者的App.tsx:

export function Dashboard() { const lastBeat = useSignalValue(heartbeat); const ms = useSignalValue(intervalMs); return (

Last heartbeat: {lastBeat?.toLocaleTimeString() ?? "—"}

Polling every {ms} ms

这里用了个叫useSignalValue的桥接Hook——信号的值被转换成React能消费的state,但信号的订阅关系还在数据层自己手里。

结果是:改intervalMs的时候,createEffect那边自动重跑定时器,Dashboard组件只收到最新的ms值,不需要关心定时器的创建和销毁。而Blinker组件里的光标,该闪还是闪,该停还是停,两条线互不干扰。

作者特意强调了行为差异:Timer polling(createEffect)独立于任何组件,页面导航时继续运行;UI blinking(useEffect)随组件挂载/卸载创建和清理。

这个设计在解决什么真问题?

熟悉React历史的人知道,useEffect的批评声音从来没停过。Dan Abramov自己写过一篇《useEffect完整指南》,底下最高赞评论是"我还是不懂"。

核心矛盾在于:useEffect被迫同时干两件事——同步外部系统(数据),和同步浏览器API(DOM)。这两件事的时序要求完全不同,但API长得一模一样,依赖数组的语义还随场景变化。


Signals方案把第一层抽走了。数据相关的副作用跟着信号走,有独立的创建-更新-销毁生命周期;UI相关的副作用留在React里,跟着渲染周期走。两边都用onCleanup,但执行的时机由各自的运行时保证。

这不是什么理论洁癖。作者举的实际场景是:一个轮询心跳,一个光标闪烁。在生产环境里,这可能是WebSocket重连策略和加载动画的关系,是后台同步状态和Toast提示的关系——以前写在一起必然互相干扰,现在可以分开测试、分开优化。

社区对这个方案的反应很分裂。一部分人觉得终于不用在useEffect里写一堆防御性代码了,另一部分人担心又多了一层概念负担。但Star数的增长是真实的,4000多个开发者用实际行动投了票。

React官方的Signals实现还在RFC阶段,具体语法变了好几稿。社区方案的优势是现在就可用,而且API设计明显借鉴了Solid.js的成熟经验——createEffect、onCleanup、signal.get()/set(),几乎照搬。

风险也有。这个方案依赖React的订阅机制做桥接,如果官方最终定的API差异太大,迁移成本不会小。但作者似乎不太在意,系列文章已经写到第四篇,每一篇都在补全边缘场景的处理。

一个值得注意的细节:作者的代码里没有任何"魔法"。signal、createEffect都是普通函数,没有编译时转换,没有Babel插件。这意味着你可以逐行调试,可以在浏览器控制台里手动调heartbeat.set()看效果。

这种可观测性在现在的前端生态里反而成了稀缺品。太多方案藏在编译器后面,开发者遇到问题只能猜。

回到开头那个问题:React团队知道社区在这么干吗?

知道。React核心成员Andrew Clark去年在Twitter上回复过类似方案,说"我们也在探索这个方向,但想确保和并发特性兼容"。翻译一下:官方认可问题存在,但解法要保守。

保守有保守的道理。React的并发渲染(Concurrent Rendering)让时机问题变得极其复杂,一个信号更新如果在渲染中途触发,会不会导致死循环?会不会破坏时间切片?

社区方案目前的答案是:createEffect在微任务队列里调度,故意不和React的渲染帧抢资源。这个 trade-off 牺牲了最低延迟,换来了安全性。够不够用,取决于你的场景。

作者没说的是:这个方案已经在某个生产环境里跑了多久、撑住了多少流量。但代码的完整度和测试覆盖率暗示这不是玩具项目——有完整的TypeScript定义,有React适配层的边界情况处理,甚至还有和Next.js App Router的兼容说明。

如果你现在就想试,作者提供了现成的模板。但更值得观察的是这个模式的演化:Signals会不会成为React的标配?官方和社区方案最终是合并还是分叉?以及,有多少开发者愿意为了"更干净的数据流"承担额外的学习成本?

最后一个问题留给正在读的你:在你的项目里,有多少useEffect其实是在管数据而不是管DOM?数清楚这个数字,可能比选哪个方案更重要。

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
夫妻吵架后,为什么一场高质量的“性生活”能让两人都爽翻天?

夫妻吵架后,为什么一场高质量的“性生活”能让两人都爽翻天?

精彩分享快乐
2026-03-10 07:50:03
霍尔木兹断航!第一个亚洲国家已断粮倒下,下一个受害者浮出水面

霍尔木兹断航!第一个亚洲国家已断粮倒下,下一个受害者浮出水面

小影的娱乐
2026-03-22 20:31:39
浙江男篮加时险胜青岛,程帅澎24+5吴前关键失误,超级外援39+7

浙江男篮加时险胜青岛,程帅澎24+5吴前关键失误,超级外援39+7

中国篮坛快讯
2026-03-28 22:06:43
店铺一夜清空?口红界“爱马仕”终止运营,网友:中产贵妇不买了

店铺一夜清空?口红界“爱马仕”终止运营,网友:中产贵妇不买了

品牌观察官
2026-03-27 17:18:30
马杜罗再次出庭 撤案请求遭驳回 “瘦了13公斤”全程未发言 特朗普暗示马杜罗还将面临更多指控

马杜罗再次出庭 撤案请求遭驳回 “瘦了13公斤”全程未发言 特朗普暗示马杜罗还将面临更多指控

每日经济新闻
2026-03-27 17:35:53
医生:脑梗最危险信号,不是嘴唇发紫,而是频繁出现这几种异常

医生:脑梗最危险信号,不是嘴唇发紫,而是频繁出现这几种异常

健康之光
2026-03-28 11:50:14
中国将迎人口死亡高峰!22年1041万,23年1100万,去年死亡多少?

中国将迎人口死亡高峰!22年1041万,23年1100万,去年死亡多少?

长歌侃娱
2026-01-16 07:55:03
香港再无董建华

香港再无董建华

华人星光
2025-11-25 12:01:27
超越东契奇!约老师56次轰下30+三双 升至历史第二位

超越东契奇!约老师56次轰下30+三双 升至历史第二位

北青网-北京青年报
2026-03-28 20:50:14
意外强援!乌干达总司令表态:以色列要是输了,我们直接出兵!

意外强援!乌干达总司令表态:以色列要是输了,我们直接出兵!

阿腩讲娱乐
2026-03-28 22:54:39
87分钟点球绝平!中国男足拿1分后,最新排名有变,主裁判罚争议

87分钟点球绝平!中国男足拿1分后,最新排名有变,主裁判罚争议

侃球熊弟
2026-03-28 20:07:16
调查发现:每天都走路的人,大多到了75岁后,身体或有5种变化

调查发现:每天都走路的人,大多到了75岁后,身体或有5种变化

医学原创故事会
2026-03-27 23:34:03
波神28+8勇士连续4季横扫奇才 桑托斯27分波杰姆斯基22+10+7

波神28+8勇士连续4季横扫奇才 桑托斯27分波杰姆斯基22+10+7

醉卧浮生
2026-03-28 12:29:17
跌麻了!笔记本开年销量暴跌40%近乎腰斩:没人买了

跌麻了!笔记本开年销量暴跌40%近乎腰斩:没人买了

中国能源网
2026-03-26 14:13:04
李小璐发视频被冲了!

李小璐发视频被冲了!

嘻笑堂
2026-03-27 15:54:11
美容院老板娘大实话:脱了衣服,女人的差距根本不在脸上!

美容院老板娘大实话:脱了衣服,女人的差距根本不在脸上!

夜深爱杂谈
2026-03-08 21:28:24
云南省发展和改革委员会享受一级调研员待遇退休干部许建平接受审查调查

云南省发展和改革委员会享受一级调研员待遇退休干部许建平接受审查调查

界面新闻
2026-03-28 15:06:35
荷兰经济部长:是我让安世脱离中国的,这一切全部为了荷兰与欧洲

荷兰经济部长:是我让安世脱离中国的,这一切全部为了荷兰与欧洲

南宗历史
2026-03-28 23:24:02
27吨冻牛头牛蹄跨省运输后部分发霉,冻库、冷链车送检样品均超标,多部门介入

27吨冻牛头牛蹄跨省运输后部分发霉,冻库、冷链车送检样品均超标,多部门介入

红星新闻
2026-03-28 19:53:39
网传《雄狮少年》出品方倒闭,当年怼网友,终于为自己的行为买单

网传《雄狮少年》出品方倒闭,当年怼网友,终于为自己的行为买单

八卦南风
2026-03-27 17:17:39
2026-03-29 00:28:49
闪存猎手
闪存猎手
全网蹲好价的野生捕手,算力与羊毛都不可辜负。
357文章数 1关注度
往期回顾 全部

科技要闻

华为盘古大模型负责人王云鹤确认离职

头条要闻

美媒:和欧盟"外长"发生激烈交锋 鲁比奥"显然很恼火"

头条要闻

美媒:和欧盟"外长"发生激烈交锋 鲁比奥"显然很恼火"

体育要闻

“我是全家最差劲的运动员”

娱乐要闻

陈牧驰陈冰官宣得子 晒一家三口握拳照

财经要闻

卧底"科技与狠活"培训:化工调味剂泛滥

汽车要闻

置换补贴价4.28万起 第五代宏光MINIEV正式上市

态度原创

数码
游戏
本地
公开课
军事航空

数码要闻

好用的男士剃须刀哪个牌子好?综合表现出众的十大剃须刀排名甄选

《死亡搁浅2》Steam销量超42万 中国玩家占一半

本地新闻

在潍坊待了三天,没遇到一个“潍坊人”

公开课

李玫瑾:为什么性格比能力更重要?

军事要闻

美军中东基地损失最新披露

无障碍浏览 进入关怀版