深入解析 React Effect 反模式将交互逻辑放入事件处理器Vercel React Best Practices 实战指南【免费下载链接】phoenixAI Observability Evaluation项目地址: https://gitcode.com/gh_mirrors/phoenix13/phoenix本文源自当前仓库 Vercel React Best Practices 技能库中的核心规则 rerender-move-effect-to-event.md。该技能库由 Vercel Engineering 维护收录了 70 余条面向 React/Next.js 的性能优化规则按影响度分为 8 大类本规则属于第 5 类Re-render Optimization重渲染优化影响度为 MEDIUM。规则速览什么情况下必须把副作用搬进事件处理器当一个副作用side effect是由某个明确的用户操作提交表单、点击按钮、拖拽元素、键盘输入等触发时应当在该操作对应的事件处理器event handler中直接执行它而不要把用户执行了操作建模为一个 state 再交给useEffect去响应。后者会让 effect 在无关状态变更时被反复触发并可能导致同一个副作用被重复执行。这是 React 官方移除 effect 依赖指南中的一个经典判例Should this code move to an event handler?这段代码是否应该移到事件处理器中。判定标准简单直接如果某段副作用代码只应当响应用户的一次具体操作而运行一次它就是事件处理器该做的事而不是 effect 该做的事。反模式剖析用 state effect 建模一次提交下面是一个典型的错误写法——把是否提交作为布尔 state再让 effect 监听它function Form() { const [submitted, setSubmitted] useState(false) const theme useContext(ThemeContext) useEffect(() { if (submitted) { post(/api/register) showToast(Registered, theme) } }, [submitted, theme]) return button onClick{() setSubmitted(true)}Submit/button }这段代码存在两个问题依赖数组使 effect 在无关变更时重复运行effect 的依赖是[submitted, theme]。theme来自ThemeContext它并不代表提交这个动作但只要主题上下文发生变化effect 就会重新运行并再次调用post(/api/register)——用户可能被重复注册。这正是本技能库中 rerender-dependencies.md 强调的问题effect 依赖越宽越容易被无关变更重新触发。语义错位submitted这个 state 除了通知 effect 执行一次副作用之外没有其他用途它是为 effect 而存在的中间人。副作用本身并没有消费任何随时间变化的数据它只是响应一次点击。正确范式副作用直接放进事件处理器function Form() { const theme useContext(ThemeContext) function handleSubmit() { post(/api/register) showToast(Registered, theme) } return button onClick{handleSubmit}Submit/button }改写后的行为完全符合预期post(/api/register)和showToast(...)只会在用户点击按钮时执行一次组件不再维护多余的submittedstate少了一次 state 写入就少触发一次重渲染不再有依赖数组theme或其他上下文的任何变化都不会误触发副作用事件处理器总是从最新一次的渲染闭包中读取theme不存在陈旧值问题。从重渲染优化的角度看这个改写同时消除了两类开销一是去掉了多余的 state 更新与随之而来的重渲染二是让副作用与组件的渲染生命周期彻底解耦。背后的原理为什么 effect 会多跑和重跑要真正理解这条规则需要澄清 effect 的两个行为特性1. effect 可能执行多次重跑useEffect的依赖数组采用引用/值比较数组中任何一个依赖项发生变化React 就会先执行上一次 effect 的清理函数再重新执行 effect。因此任何被塞进依赖数组的无关值如theme都等于给副作用装了一个意外扳机。2. 开发环境下 effect 会被有意地双执行React 在开发模式配合StrictMode下会故意把 effect 挂载→卸载→再挂载以便暴露未正确清理的副作用。也就是说把提交注册这种不可幂等的操作放进 effect即使在依赖数组完全正确的情况下开发环境里也可能看到副作用执行两次而事件处理器永远不会被 React 以这种方式重复调用。把操作放入事件处理器是让一次用户操作 一次副作用的唯一可靠保证。3. effect 是渲染后的异步补丁事件处理器是交互的同步应答Effect 在浏览器绘制后运行用于把组件与外部系统订阅、计时器、网络请求等同步而事件处理器直接对应一次用户意图。前者适合持续存在的关系后者适合一次性动作。何时该用事件处理器、何时该保留 effect判定清单场景该用事件处理器该用 effect用户点击提交/保存/删除按钮✅ 直接在 onClick 中执行❌拖拽结束、选中变化后的即时反馈✅❌订阅外部事件源socket、storage、窗口事件❌✅ 并在清理函数中取消订阅组件挂载时加载初始数据❌✅或用数据请求库对值的变化过程做持续响应如宽度变化触发布局切换❌✅ 但要收窄依赖仅需记录一次分析/埋点事件✅❌核心判据来自本规则的标题本身这段副作用代码是否绑定在一个具体的用户动作上是——进事件处理器否它是对某个持续变化的数据流的响应——保留 effect同时遵循 rerender-dependencies.md 的收窄依赖原则// 属于持续响应保留 effect但依赖用原始值收窄 const isMobile width 768 useEffect(() { if (isMobile) { enableMobileMode() } }, [isMobile]) // 只在该布尔值翻转时触发而不是每次 width 变化与技能库中相邻规则的配合使用这条规则不是孤立的它与本技能库 Re-render 优化分类SKILL.md 第 5 节中的其他规则共同构成一套完整的去 effect 化方法论rerender-derived-state-no-effect.md如果值能从现有 props/state 计算得出就在渲染期间直接推导而不是存进 state 再靠 effect 同步。与本规则同理——能省掉的 effect 就不要创建。rerender-functional-setstate.md事件处理器内若需要基于当前 state 更新状态应使用函数式更新setItems(curr ...)避免把 state 值放进依赖数组造成闭包陈旧或回调频繁重建。rerender-dependencies.md对于确实需要保留的 effect依赖必须收窄到原始值/布尔派生值减少重跑机会。advanced-effect-event-deps.md与advanced-event-handler-refs.md在必须在 effect 内部注册回调如订阅事件源但回调要读取最新值的少数场景中使用useEffectEvent或在 ref 中保存最新 handler避免把回调函数本身放入依赖数组导致 effect 每次渲染都重订阅——这是事件处理器优先原则在 effect 边界内的延伸解法。例如监听窗口事件的 hook 若把 handler 直接放进依赖数组会在每次渲染后重新订阅改用 ref 或useEffectEvent后订阅只依赖event名称稳定且不会漏读最新 handlerimport { useEffect, useEffectEvent } from react function useWindowEvent(event: string, handler: (e: Event) void) { const onEvent useEffectEvent(handler) useEffect(() { window.addEventListener(event, onEvent) return () window.removeEventListener(event, onEvent) }, [event]) // 注意依赖数组里是 event不是 onEvent }实战自查清单在代码评审或重构时遇到useEffect可以先问自己四个问题对应规则原文件的判定要点触发源是谁如果只有用户点了某个按钮/提交了表单才会需要执行立刻把逻辑搬进事件处理器依赖数组里有没有陪跑依赖如果有与副作用无关的上下文值说明这个 effect 本就不该存在或需要像收窄[submitted, theme]那样大幅收窄这个 state 是不是只为 effect 而生如果删掉它 effect 就不需要了说明它是多余的中间人状态应一并删除开发环境双执行能否接受提交、支付、发送通知等不可幂等的操作放在 effect 里在 StrictMode 下可能被调用两次事件处理器是更安全的落点。结语把交互逻辑放进事件处理器是 React 组件开发中性价比最高的性能与正确性改进之一它同时减少了 state 数量、减少了重渲染次数、消除了依赖数组带来的意外副作用重跑并保证了一次操作只产生一次副作用的语义。在维护 React/Next.js 代码库时将这条规则与 rerender-derived-state-no-effect.md、rerender-dependencies.md、rerender-functional-setstate.md 等规则组合使用即可系统性地消除由错误 effect 用法带来的重渲染与重复副作用问题。完整规则集可在仓库 .agents/skills/vercel-react-best-practices/ 目录下查看。【免费下载链接】phoenixAI Observability Evaluation项目地址: https://gitcode.com/gh_mirrors/phoenix13/phoenix创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
企业数字化 ERP 产品动态
相关推荐
一个项目挂 3 个仓库:Codex 多文件夹项目与跨仓 Diff 怎么审 一个项目挂 3 个仓库:Codex 多文件夹项目与跨仓 Diff 怎么审 [!NOTE] ChatGPT 桌面应用的本地 Project 可以附加多个文件夹,并指定一个 Primary folder;Codex 能读写所有附加目录,但自动发现 AGENTS.md、Skills、config.toml 和默认 Git 操作仍以主目录为中心。 Review pan… · 2026/9/24 17:06:41
Agarose-Resin,Sepharose-4B,琼脂糖凝胶基质的理化性质 一、产品基本属性产品名称: Sepharose 4B
中文名称: Sepharose 4B琼脂糖凝胶基质
通用名称: 4% Agarose Gel Filtration Matrix / Agarose Resin
基质组成: 约4%琼脂糖
CAS号: 9012-36-6(琼脂糖)… · 2026/9/24 17:06:41
Comp AI CRM 中 AI Elements 附件组件实战:Attachments 组合式文件展示方案 后端前端CRM人工智能AI Agent 【免费下载链接】crm Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM. 项目地址: https://gitcode.com/gh_mirrors/crm48/crm 点击查看 免费下载 Attachments 是 AI Elements 组件库中负责"附件… · 2026/9/24 17:06:22
『无为则无心』Python函数 — 30、Python变量的作用域 1、作用于的概念变量作用域指的是变量生效的范围, 在中一共有两种作用域。函数作用域的第二部分我们来讨论一下局部变量。所谓局部变量呢, 它就是被定义在函数体内部的那些变量, 也就是只在函数体内部有效。def testA():# 局部变量a# 在函数中为变量赋值时,默认都是… · 2026/9/24 17:44:21
没错,最强的Python编辑器还是它 为什么说才是搞的真爱,没用过的人可能体会不到所有人都在讨论, 哪一个写代码时最好用? 大家争论得非常激烈。许多人认为它是最厉害的工具。事实果真如此吗? 有人站出来说, 那个更轻巧灵活, 而且插件很多。这种说法听起来确实有道理。但是它的特点依然很突出, 它是… · 2026/9/24 17:44:21
函数:写一次代码,用一万次 如果你连函数都写不出来, 那么你根本就没有进入到这门学问的门槛里面去。函数, 它是编程里面的那个核心的抽象。你现在去写一个函数, 这就等于你去发明了一个新的动词。那其他的人, 包括那个未来的你自己在内, 也只需要去知道它是做什么事情的就行了。至于说它到底是怎么去做的… · 2026/9/24 17:44:21
手机就能学Python!无需高配置电脑,安卓与iOS都可以,30天通关 许多想要进入编程行业并从头开始学习的朋友, 往往因为电脑配置方面的问题, 而感到受阻, 并停下了自己的脚步, 他们觉得必须先购买高性能的笔记本电脑或者安装复杂的操作系统之后, 才能启动他们的学习之路, 然而实际上这样做是完全没必要的, 咱们日常随身携带并且放在口袋里的手… · 2026/9/24 17:44:21
python中setup的含义 在开发程序的这个过程中, setup这个词汇的所代表的基本含义是去使用setup.py这个特定的文件来处理程序的事, 具体包括对程序进行打包、构建以及安装这几项操作, 因为借助于setup.py文件的存在, 人们能够对于程序所必须的依赖环境、具体的版本号, 还有作者的各类信息等进行明确的… · 2026/9/24 17:44:21
Python量化交易数据接口开发 在量化交易这个大领域里面, 数据接口的开发是一个特别关键的步骤。作为现在很多人都在用的编程语言, 它因为高效还有容易学到手这些优点, 所以被大量地用在量化交易的场景里去。接下来这篇文章会详细给大家解释清楚到底应该怎样去动手开发量化交易里的数据接口, 这里头包括了怎… · 2026/9/24 17:44:02
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44