<span class=“js_title_inner“>React 19.2:用 useEffectEvent 告别闭包陷阱</span>

React Hook详解 - useState、useEffect、useContext、useRef、自定义hook 在上面的操作中都不用清理的副作用,然而,有些副作用是需要去清理的,不清理会造成异常甚至内存泄漏,比如开启定时器,如果不清理,则会多次开启,从上面可以看到。因为涉及到 state 的状态合并,react 认为当你在事件绑定中操作 state 是非常频繁的,所以为了节约性能 react 会把多次。类似,可以接收一个新的 state 值更新,也可以函数式更新。进行合并为一次,最后在一次性的更新 state,而定时器里面操作 state 是不会把多次合并为一次更新的。返回一个state,以及更新state的函数。 阅读详情


React 19.2 最让人期待的更新就是正式稳定的 useEffectEvent。这个新钩子专门解决困扰我们已久的闭包问题,从此不用再手动用 useRef 同步状态了。

表单自动保存的痛点

先看一个常见场景:用户输入时,需要实现“停止输入1秒后自动保存”的功能。

import { useState, useEffect } from 'react';

function ProfileEditor() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });
  const [status, setStatus] = useState('idle');

  useEffect(() => {
    const timer = setTimeout(async () => {
      setStatus('saving');
      try {
        // 这里有个坑:formData 可能不是最新的
        await fetch('/api/profile', {
          method: 'POST',
          body: JSON.stringify(formData)
        });
        setStatus('saved');
      } catch (err) {
        setStatus('error');
      }
    }, 1000);

    return () => clearTimeout(timer);
  }, [formData]); // 必须依赖 formData

  return (
    <form>
      <input
        value={formData.name}
        onChange={(e) => setFormData(prev => ({ ...prev, name: e.target.value }))}
      />
      <input
        value={formData.email}
        onChange={(e) => setFormData(prev => ({ ...prev, email: e.target.value }))}
      />
      <div>{status}</div>
    </form>
  );
}

这段代码有个明显问题:每次输入都会重启定时器。用户快速打字时,定时器频繁创建销毁,既浪费性能又可能导致保存顺序错乱。

如果去掉 formData 依赖,又会陷入闭包陷阱——定时器里的 formData 永远是初始值。

之前我们只能用 useRef 来绕过这个问题:

function ProfileEditor() {
  const [formData, setFormData] = useState({ name: '', email: '' });
  const [status, setStatus] = useState('idle');
  const formRef = useRef(formData);

  // 手动同步最新值
  useEffect(() => {
    formRef.current = formData;
  }, [formData]);

  useEffect(() => {
    const timer = setTimeout(async () => {
      setStatus('saving');
      try {
        // 从 ref 获取最新值
        await fetch('/api/profile', {
          method: 'POST',
          body: JSON.stringify(formRef.current)
        });
        setStatus('saved');
      } catch (err) {
        setStatus('error');
      }
    }, 1000);

    return () => clearTimeout(timer);
  }, []); // 依赖可以为空了

  // ... 渲染部分相同
}

这种写法能解决问题,但每次都要手动同步 ref,代码冗余还容易出错。

useEffectEvent 的解决方案

useEffectEvent 的设计很巧妙:把 effect 里的逻辑提取出来,让它能自动获取最新状态,同时不影响 effect 的依赖关系。

用 useEffectEvent 重写上面的例子:

import { useState, useEffect, useEffectEvent } from 'react';

function ProfileEditor() {
  const [formData, setFormData] = useState({ name: '', email: '' });
  const [status, setStatus] = useState('idle');

  // 用 useEffectEvent 包装保存逻辑
  const saveForm = useEffectEvent(async () => {
    setStatus('saving');
    try {
      // 这里总能拿到最新的 formData
      await fetch('/api/profile', {
        method: 'POST', 
        body: JSON.stringify(formData)
      });
      setStatus('saved');
    } catch (err) {
      setStatus('error');
    }
  });

  // effect 只负责定时逻辑
  useEffect(() => {
    const timer = setTimeout(saveForm, 1000);
    return () => clearTimeout(timer);
  }, []); // 依赖为空,effect 只运行一次

  // ... 渲染部分不变
}

这段代码解决了两个关键问题:

  1. saveForm 始终能获取最新的 formData

  2. useEffect 不会因为 formData 变化而重复执行

实现原理

useEffectEvent 创建的函数有个特殊能力:每次执行时都能穿透到当前的渲染上下文,获取最新的状态和 props。但对 effect 来说,这个函数的引用始终不变,所以不需要加入依赖数组。

使用限制

  1. 只能在 effect 内调用:useEffectEvent 返回的函数不能在事件处理函数中直接使用,比如 onClick={myEvent} 会报错。

  2. 不能作为组件属性传递:React 无法保证函数执行时的上下文一致性。

  3. 避免循环依赖:如果多个 useEffectEvent 函数相互调用,要小心循环依赖问题。

实战案例:聊天室消息过滤

useEffectEvent 在实时数据处理中特别有用。比如聊天室需要根据当前用户ID过滤消息:

function ChatRoom({ roomId, currentUserId }) {
  const [messages, setMessages] = useState([]);

  const handleNewMessage = useEffectEvent((newMsg) => {
    // 自动获取最新的 currentUserId
    if (newMsg.senderId !== currentUserId) {
      setMessages(prev => [...prev, newMsg]);
    }
  });

  useEffect(() => {
    const socket = new WebSocket(`wss://chat.example.com/${roomId}`);
    socket.onmessage = (e) => {
      const msg = JSON.parse(e.data);
      handleNewMessage(msg);
    };

    return () => socket.close();
  }, [roomId]); // 只在 roomId 变化时重连

  return (
    <div>
      {messages.map(msg => (
        <div key={msg.id}>{msg.content}</div>
      ))}
    </div>
  );
}

这个例子中,即使用户切换账号导致 currentUserId 变化,handleNewMessage 也能拿到最新值,同时不会重建 WebSocket 连接。

与社区方案的对比

在 useEffectEvent 之前,社区常用 useEventCallback:

function useEventCallback(fn) {
  const ref = useRef(fn);
  useEffect(() => { ref.current = fn; }, [fn]);
  return useCallback((...args) => ref.current(...args), []);
}

两种方案的主要区别:

特性

社区版 useEventCallback

useEffectEvent

依赖管理

需要手动声明依赖

自动捕获依赖

执行时机

同步更新,可能有时序问题

React 调度,确保状态最新

并发兼容

可能状态不一致

完全兼容并发渲染

调用限制

无限制

只能在 effect 内使用

社区方案本质是用 ref 模拟,而 useEffectEvent 是 React 原生支持,更适合复杂场景。

React 19.2 的其他更新

Activity 组件:保留状态的条件渲染

之前用 {isOpen && <Component />} 会导致组件卸载和状态丢失。Activity 组件解决了这个问题:

import { Activity } from 'react';

function UserPanel() {
  const [showDetails, setShowDetails] = useState(false);

  return (
    <div>
      <button onClick={() => setShowDetails(!showDetails)}>
        切换详情
      </button>
      {/* 隐藏时不卸载,保留所有状态 */}
      <Activity mode={showDetails ? 'visible' : 'hidden'}>
        <UserDetails /> {/* 表单输入不会丢失 */}
      </Activity>
    </div>
  );
}

缓存信号管理

服务端组件中,cacheSignal 可以管理缓存生命周期:

import { cache, cacheSignal } from 'react';

const fetchUser = cache(async (userId) => {
  const controller = new AbortController();
  const signal = cacheSignal(controller.signal);

  const res = await fetch(`/api/users/${userId}`, { signal });
  return res.json();
});

function UserProfile({ userId }) {
  const user = fetchUser(userId);
  return <h1>{user.name}</h1>;
}

缓存失效时自动中止请求,避免资源浪费。

部分预渲染优化

19.2 改进了部分预渲染,支持先输出静态内容再流式传输动态部分:

import { renderToPipeableStream } from 'react-dom/server';

app.get('/', (req, res) => {
  const stream = renderToPipeableStream(<App />, {
    onShellReady() {
      // 先发送静态框架
      res.writeHead(200, { 'Content-Type': 'text/html' });
      stream.pipe(res);
    }
  });
});

提升首屏加载速度,特别适合内容型网站。

迁移建议

  1. 先处理警告:用 useEffectEvent 替换有依赖警告的 useRef 代码

  2. 精简依赖:迁移后检查并移除不必要的依赖

  3. 配合 ESLint:使用最新版 eslint-plugin-react-hooks 检查使用规范

  4. 渐进迁移:不需要一次性重写所有代码,新功能中优先使用

总结

useEffectEvent 虽然只是一个小钩子,但解决了 React 开发中的老大难问题。它的出现让闭包陷阱成为历史,让我们能更专注于业务逻辑。

配合 Activity 组件、缓存信号等新特性,React 19.2 在并发渲染时代提供了更完善的开发体验。如果你还在为 effect 依赖头疼,不妨试试 useEffectEvent——它会成为你的新利器。

React 19.2 useEffectEvent:解决闭包陷阱的新方案 React Hooks 是现代化前端开发中管理组件状态和副作用的革命性方案,其中 useEffect 是最常用的 Hook 之一。然而,开发者在使用 useEffect 时经常会遇到闭包陷阱问题,即 effect 函数捕获的是变量初始值而非最新值。React 19.2 引入的 useEffectEvent 正是为了解决这一痛点而生。这个新 Hook 通过创建稳定的函数引用,确保回调始终能访问最新状态,同时避免不必要的 effect 重新执行。在定时器、WebSocket 连接、事件监听等需要长期运行且访问最 阅读详情

相关推荐

Win10如何使用BC3.1精简版

Win10如何使用BC3.1精简版 使用Borland C++3.1 学习μC/OS—Ⅱ几乎是现在的普遍做法,但是由于Win10系统不能直接使用BC3.1,所以需要用到DOSBox。 1、解压bc3.1精简版压缩包,本文将BC3.1文件夹复制在D盘下,DOSBOX按照默认路径安装。下图为两者安装目录图示: 2、修改DOSBOX的配置文件,找到DOSBox文件安装路径,记事本方式打开DOSBox 0.74-3 Options.bat文件,在此文件末尾添加: MOUNT C D:\BC//意为把D:\BC目录

qq_45547236的博客 2376

useEffect从入门到入土

副作用是相对于纯函数概念来说的, 除事件回调处理副作用,其他副作用尽量放在useEffect中;useEffect清理工作:卸载组件时;下一次更新前清理作用域。

小白目的博客 840

qt使用qxlsx实现xlsx、xls表格文件快速写入和读取

新年第二篇文章,收尾一个去年计划中发表文章和实现功能。

kbq的博客 1万+

在react稳定版本中使用useEffectEvent

我新建了一个项目将react包升级成实验版本后,发现在项目中仍然没法使用,而且还新出来更多的问题,so,项目中别轻易尝试。但目前experimental_useEffectEvent 还在 建设中,在react的稳定版本中还没办法使用,官网给的解决方案是,将稳定react 稳定版本升级成实验版本。react 新发布的版本18.2.0 中新增了。用于构建 Web 和原生交互界面的库。Timer.tsx 运行截图。

weixin_44405693的博客 6594

旋转矩阵推导

推导前提: 坐标使用右手坐标系,角度逆时针旋转为正。 绕X轴旋转角度为 俯仰角 即Pitch 绕Y轴旋转角度为 偏航角 即Yaw(Head) 绕Z轴旋转角度为 翻滚角 即Roll 一、平面二维坐标点的旋转 如右图所示,根据三角函数关系,可以列出向量OP与OP’的坐标表示形式: x = |OP|•cosα x′ = |OP|•cos(α+β) y = |OP|•sinα y′ = |OP|•sin(α+β) 将P′(x′,y′)表达式展开: x′ =

suixinger_lmh的随笔 1384

AnolisOS网卡驱动源码编译与安装实战指南

本文提供了一份详尽的AnolisOS网卡驱动源码编译与安装实战指南。针对系统自带通用驱动不兼容导致网络无法使用的问题,文章从环境准备、源码获取、编译安装到新旧驱动替换与持久化配置,一步步解析了如何通过源码编译安装官方驱动,解决硬件兼容性问题,并确保驱动在系统重启后自动生效。

weixin_28358083的博客 304

useEffect使用指南

最近在写一些React的应用,用上了最新的Hooks。Hooks好用,但是对于刚上手Hooks的小伙伴来说,坑也挺多的。所以决定总结一下Hooks的使用经验,从useEffect开始。useEffect用于处理组件中的effect,通常用于请求数据,事件处理,订阅等相关操作。这里以数据请求为例,来深入介绍useEffect的用法。 最基本的使用 首先,举一个简单的例子: import Re...

大灰狼的小绵羊哥哥的博客 6万+

7、初级视觉皮层(V1)的研究综述

本文综述了初级视觉皮层(V1)的基本功能特性、计算理解、细胞相互作用及理论模型。V1不仅执行方向和空间频率的选择性滤波,还通过相位选择性和上下文调制参与高级视觉处理。研究从信息编码角度解释了V1感受野的形成,并介绍了基于自然图像统计的建模方法。横向与反馈连接揭示了V1在全局视觉整合中的作用,尤其是上下文对细胞响应的快速调节。李的动态模型提出V1可生成视觉显著性图,支持前注意分割和注意力引导。未来研究聚焦于V1在注意力、意识和硬件实现中的潜在应用,推动视觉神经科学与人工智能的交叉发展。

7z8x2c4v6b9的博客 144

useEffectEvent 如何永久解决闭包陷阱与过时闭包问题?

写React代码时,你是否曾遇到过这样的“灵异事件”?明明更新了状态,可在某个回调函数里拿到的却是老数据;又或者组件卸载了,函数还在执着地使用着过时的引用,最终导致各种难以调试的错误。这些让人抓狂的情况,大多是闭包陷阱与过时闭包在作祟。别担心!`useEffectEvent`就像一剂“强力布洛芬”,能精准“止痛”,帮我们永久解决这些恼人的问题,让代码运行得丝滑顺畅。接下来,我们就一起深入了解`useEffectEvent`是如何做到的。

前端大白话 1781

【数据集】全球首个10米分辨率精细分类土地覆盖数据集GLC_FCS10

【数据集】全球首个10米分辨率精细分类土地覆盖数据集GLC_FCS10

WW、forever的博客 1567

【Mode】FDE 求解器工作流程

Lumerical---FDE(Finite Difference Eigenmode) Solver

u011699626的博客 4940

React 19.2的useEffectEvent为什么限制这么多?一篇文章讲透

而handleUserUpdate里的ref是在useLayoutEffect里更新的,它要等到Child的effect全部跑完,才轮到它更新。所以当handleChange的ref被更新的时候,onUserChange(来自props)已经是最新的了。render比effect快呀,render的时候ref还没更新,你拿到的肯定是旧值。我给你一个Hook,可以包装你的函数,使得这个函数的引用永远不变,但它总能访问最新的值。这样外部看到的函数引用永不变,但实际调用的时候,调用的是ref里的最新函数。

前端达人 1241

通信算法之320:DJI无人机云哨--信息包类型

大疆DJI无人机的云哨信息包主要分为四种类型:1)含经纬度位置、型号及序列号的完整数据包;2)仅含序列号的简化数据包;3)完全加密的数据包;4)加密密钥包。这些信息包格式不同,解析方式和应用场景也各异。如需更详细的技术资料,可联系作者获取。

leegang12的专栏 759

React 终于出手了:彻底终结 useEffect 的“闭包陷阱“

Cloudflare 的例子不是危言耸听,我自己也遇到过类似的问题——一个看起来无害的对象放进依赖数组,结果页面卡死。+ "自动同步" 的封装,只是语义更清晰:这是一个"事件",它描述的是"发生某事时要做什么",而不是 effect 本身的逻辑。在编译器的情况下,它们稳定对象引用,有助于减少围绕对象标识的潜在 bug。不要听信反对者的声音——React 并没有停滞不前,它还在进化,而且在大多数情况下,变得更好。我知道这个名字很绕口,但当涉及到稳定你的 React 应用时,它是一个救命稻草。

W11929311582的博客 693

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

react - useEffect函数的理解和使用

React的useEffect Hook可以替代类组件的生命周期方法,它能在函数组件中执行副作用操作。useEffect接收两个参数:回调函数和依赖项数组。根据第二个参数的不同,useEffect会有不同的表现:不传参数会监听所有状态变化;空数组相当于componentDidMount;指定状态则只监听该状态;返回回调函数相当于componentWillUnmount。useEffect可以在组件中多次使用,建议将不同业务逻辑放在不同的useEffect中处理。需要注意的是,无论参数如何,useEffect

尽日寻春不见春,芒鞋踏遍陇头云。归来笑拈梅花嗅,春在枝头已十分。 9340

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

React 19.2:用 useEffectEvent 告别闭包陷阱

配合 Activity 组件、缓存信号等新特性,React 19.2 在并发渲染时代提供了更完善的开发体验。它的出现让闭包陷阱成为历史,让我们能更专注于业务逻辑。把 effect 里的逻辑提取出来,让它能自动获取最新状态,同时不影响 effect 的依赖关系。但对 effect 来说,这个函数的引用始终不变,所以不需要加入依赖数组。先看一个常见场景:用户输入时,需要实现“停止输入1秒后自动保存”的功能。在实时数据处理中特别有用。每次执行时都能穿透到当前的渲染上下文,获取最新的状态和 props。

tonylua的博客 488

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

React19.2重磅更新:终于解决useEffect依赖数组难题

实用层面- 状态保留场景的官方解决方案- 重构复杂useEffect的利器,提升代码可读性和稳定性SSR 性能自动优化,无需配置。

2401_86373285的博客 882

终于!React 又发新版本了

React 19.2 没有惊天动地的新概念,但它解决了不少日常开发中的痛点:•让复杂页面切换时更顺滑,不用担心状态丢失;•化解了老生常谈的依赖数组问题;•性能轨迹(Performance Tracks)让调试不再像摸黑;•SSR 的升级也让大规模项目更能跑得动、跑得快。如果你的项目里有 表单场景多、副作用逻辑复杂,或者依赖 SSR 性能,升级到 19.2 能带来不少实打实的好处。

欢迎来到我的博客 1243

React 19.2正式发布啦!

React 19.2 虽非颠覆性版本,却以“小版本大价值”的定位,精准解决了开发者日常工作中的多个核心痛点。通过几大核心特性的协同发力,它在客户端体验优化与架构能力提升两方面形成了闭环,为复杂应用开发提供了更流畅、更可控的技术支撑。

2501_92872533的博客 1374

useEffectEvent 的持久闭包如何避免过时状态引用?

React 闭包陷阱是异步回调中常见的问题,会导致函数捕获过时状态值。useEffectEvent 是 React 18 提供的解决方案,它能创建持久回调函数,始终访问最新状态而非捕获时的值,有效避免闭包问题。相比 useRef 临时方案更优雅,适用生产环境。本文通过代码示例对比了不同方案的优缺点,并通俗解释其原理:闭包就像保存旧电话号码,而 useEffectEvent 自动更新为最新号码,确保永远能联系到"最新状态"。该 API 特别适合处理异步操作和事件处理中的状态依赖问题。

前端大白话 1351

一个新的React概念:Effect Event

Event:由某些行为触发,而不是状态变化触发的逻辑Effect:由某些状态变化触发的,而不是某些行为触发的逻辑:在Effect内执行,但Effect并不依赖其中状态的逻辑其中在React中的具体实现是。相比于他的前身useEvent,他附加了2条限制:只能在Effect内执行始终返回不同的引用在我看来,的出现完全是由于Hooks实现机制上的复杂性(必须显式指明依赖)导致的心智负担。毕竟,同样遵循Hooks理念的就没有这方面问题。

IT界那些事儿 451

React之useEffect

在React中,useEffect是一个非常重要的Hook,它用于管理副作用操作。副作用指的是那些不直接与组件渲染相关的操作,例如数据获取、订阅、手动DOM操作等。本文将详细介绍useEffect的概念、基础使用、参数说明以及如何清除副作用,并提供多个实用的使用案例。

m0_72560900的博客 934

React 19.2:useEffect终于要被“架空“了?Meta这波操作背后的真实意图

但代价是——你需要更清楚地**区分什么是"响应式状态",什么是"事件回调"**。:这是React在向Next.js等元框架"献媚"。别想了,老老实实用框架的封装版本。当你打开React 19.2的更新日志,第一反应可能是:又来了,Meta又在发明新API了。以前你只能看到一个黑盒的"Scripting"阶段,现在能精确定位到哪个组件在拖后腿。更直白点说,Meta正在用一整套新API体系,悄悄"架空"你熟悉的那些Hook。⚡ 第三刀:Partial Pre-rendering——SSR的"降维打击"

前端达人 417

三、useEffect的使用

文章目录一、useState简介和小案例二、useEffect的作用三、为什么在组件内部调用useEffect四、useEffect会什么时候执行 一、useState简介和小案例 useEffect可以让你在函数组件中执行副作用操作(副作用:和函数业务主逻辑关联不大,特定时间或事件中执行的动作,比如Ajax请求后端数据,添加登录监听或取消登录、手动修改DOM等),useEffect在每次状态(state)更新时执行,主要用来代替常用的生命周期函数。我们为计数器增加一个小功能:将document的title

qq_44753552的博客 2967

React 事件订阅的稳定引用问题:从 useEffect 到 useEffectEvent

React中订阅WebSocket时,handler直接放入effect依赖会导致频繁重订阅问题。本文提出三种解决方案:1)使用useRef代理最新handler(存在Strict Mode风险);2)分离effect同步(安全但复杂);3)React 19的useEffectEvent(最优解)。关键是通过稳定引用避免不必要的重订阅,同时确保始终能访问最新handler。文章还强调subscribe函数本身需要稳定,建议用useCallback包裹。

开源,让世界更美好 381

React 19.2 重磅更新!这几个新特性终于来了

大家好,我是 Immerse,一名独立开发者、内容创作者、AGI 实践者。,获取最新文章(更多内容只在公众号更新)也同步更新。转载请在文章开头注明出处和版权信息。我会在这里分享关于编程独立开发AI干货开源个人思考等内容。如果本文对您有所帮助,欢迎动动小手指一键三连(评论转发React 官方刚发布了 19.2 版本这次更新不是小修小补,而是带来了几个让人期待已久的功能。整理一下,分享给大家。

Immerse的 Blog 1080
上一篇: <span class=“js_title_inner“>畅玩本地编程大模型:解锁 VSCode 与 NeoVim 高效编程新姿势</span>
下一篇: React 19.2:用 useEffectEvent 告别闭包陷阱
云前端
博客等级 码龄17年 101粉丝 113原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值