一、React 闭包陷阱详解

1. 什么是闭包陷阱

React 闭包陷阱是指在函数组件中使用 Hook(特别是 useEffect 和 useCallback)时,由于闭包特性导致访问到旧的 state 或 props 值,而非最新值的现象。

2. 典型场景示例

function Counter() {const [count, setCount] = useState(0);useEffect(() => {const timer = setInterval(() => {console.log(count); // 这里总是打印初始值0setCount(count + 1);}, 1000);return () => clearInterval(timer);}, []); // 空依赖数组return <div>{count}</div>;
}

3. 产生原因分析

  • 闭包特性:Effect 回调函数捕获了初始渲染时的 count 值

  • 依赖数组:空数组意味着 Effect 只在挂载时运行一次

  • 每次渲染独立:每次渲染都有独立的 props/state 和 Effect

二、解决闭包陷阱的 5 种方法

1. 正确声明依赖项

useEffect(() => {const timer = setInterval(() => {setCount(count + 1);}, 1000);return () => clearInterval(timer);
}, [count]); // 添加count依赖

2. 使用函数式更新

useEffect(() => {const timer = setInterval(() => {setCount(prev => prev + 1); // 使用前一个值}, 1000);return () => clearInterval(timer);
}, []); // 不需要添加count依赖

3. 使用 useRef 保存可变值

function Counter() {const [count, setCount] = useState(0);const countRef = useRef(count);useEffect(() => {countRef.current = count; // 每次渲染更新ref});useEffect(() => {const timer = setInterval(() => {console.log(countRef.current); // 访问最新值setCount(countRef.current + 1);}, 1000);return () => clearInterval(timer);}, []);return <div>{count}</div>;
}

4. 使用 useReducer

function reducer(state, action) {if (action.type === 'tick') {return state + 1;}throw new Error();
}function Counter() {const [state, dispatch] = useReducer(reducer, 0);useEffect(() => {const timer = setInterval(() => {dispatch({ type: 'tick' });}, 1000);return () => clearInterval(timer);}, []); // 不需要依赖return <div>{state}</div>;
}

5. 自定义 Hook 封装

function useInterval(callback, delay) {const savedCallback = useRef();useEffect(() => {savedCallback.current = callback;});useEffect(() => {function tick() {savedCallback.current();}if (delay !== null) {const id = setInterval(tick, delay);return () => clearInterval(id);}}, [delay]);
}// 使用
function Counter() {const [count, setCount] = useState(0);useInterval(() => {setCount(count + 1);}, 1000);return <div>{count}</div>;
}

三、React 16/17/18 主要区别

React 16.x 主要特性

  1. Fiber 架构(16.0)

    • 新的协调算法

    • 支持异步渲染(但默认未启用)

  2. Hooks(16.8)

    • useState, useEffect 等 Hook API

    • 函数组件能力大幅增强

  3. Error Boundaries(16.0)

    • componentDidCatch 生命周期

    • 更好的错误处理机制

  4. Context API(16.3)

    • 新的 Context API 设计

    • 替代旧版 context

React 17.x 主要特性

  1. 事件委托变更(17.0)

    • 事件不再附加到 document,而是附加到 root DOM

    • 解决多版本 React 共存问题

  2. 渐进式升级(17.0)

    • 更容易逐步升级 React 版本

    • 为 React 18 做准备

  3. 新的 JSX 转换(17.0)

    • 无需引入 React 即可使用 JSX

    • 自动从 react/jsx-runtime 导入

  4. Effect 清理时机(17.0)

    • useEffect 清理函数改为异步执行

    • 与 componentWillUnmount 行为一致

React 18.x 主要特性

  1. 并发渲染(18.0)

    • 新的 createRoot API

    • startTransition, useTransition

    • useDeferredValue

  2. 自动批处理(18.0)

    • 自动合并多个状态更新

    • 包括 Promise、setTimeout 等

  3. 新的 Hook(18.0)

    • useId:生成唯一 ID

    • useSyncExternalStore:外部存储集成

    • useInsertionEffect:CSS-in-JS 库使用

  4. 流式 SSR(18.0)

    • Suspense 支持服务端渲染

    • 选择性注水(Selective Hydration)

四、版本升级对比表

特性React 16React 17React 18
架构FiberFiber并发Fiber
Hook支持16.8+
事件系统documentrootroot
批处理仅React事件仅React事件全自动
SSR传统传统流式+选择性注水
新APIContext, Error BoundariesTransition, Suspense等
JSX转换经典新/经典
默认渲染模式同步同步并发可选

五、实际开发建议

  1. 闭包陷阱防范

    • 始终检查 Hook 依赖数组

    • 优先使用函数式更新

    • 复杂场景使用 useReducer

  2. 版本选择建议

    • 新项目直接使用 React 18

    • 现有项目逐步升级到 18(通过 17 过渡)

    • 需要 IE11 支持的项目可停留在 17

  3. 升级注意事项

    # 从16/17升级到18的步骤
    npm install react@18 react-dom@18
    # 修改入口文件
    import { createRoot } from 'react-dom/client';
    const root = createRoot(document.getElementById('root'));
    root.render(<App />);
  4. 并发特性采用策略

    // 逐步采用并发特性
    function App() {const [isPending, startTransition] = useTransition();const handleClick = () => {startTransition(() => {// 非紧急状态更新setResource(fetchData());});};return (<Suspense fallback={<Spinner />}><Component /></Suspense>);
    }

理解 React 闭包陷阱和版本差异有助于编写更健壮的 React 应用,并合理规划项目升级路线。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917338.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917338.shtml
英文地址,请注明出处:http://en.pswp.cn/news/917338.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[BJDCTF2020]EasySearch

首先尝试了一下sql注入&#xff0c;但是没有找到不同回显。直接用sqlmap扫描一下&#xff0c;因为这边用的是POST请求&#xff0c;所以需要抓包将请求复制到txt文件中然后使用命令sqlmap -p bp.txt。也没有发现注入漏洞。 再进行目录扫描试试&#xff1a; [02:33:43] 403 - …

【Linux】基本指令的使用 and 面试常问

1、man 指令使用方法&#xff1a;man Linux指令。功能&#xff1a;相当于字典&#xff0c;查找指令的用法。常用选项&#xff1a;-k&#xff1a;根据关键字搜索联机帮助。num&#xff1a;只在第num章节查找。-a&#xff1a;将所有章节的都显示出来&#xff0c;比如man printf它…

零基础 “入坑” Java--- 十六、字符串String 异常

文章目录一、String1.字符串的不可变性2.字符串的修改3.StringBuilder和StringBuffer4.【字符串练习】4.1 字符串中的第一个唯一字符4.2 字符串最后一个单词的长度4.3 验证回文串二、异常1.初识异常2.异常的分类3.异常的处理4.异常处理流程总结5.自定义异常在上一章节中&#x…

梯度下降在大模型训练中的作用与实现

梯度下降&#xff08;Gradient Descent&#xff09;是深度学习中最核心的优化算法之一。大模型&#xff08;如GPT、BERT&#xff09;在训练时需要优化数十亿甚至上千亿的参数&#xff0c;而梯度下降及其变体&#xff08;如SGD、Adam&#xff09;正是实现这一优化的关键工具。它…

【JVS更新日志】开源框架、APS排产、企业计划、物联网、逻辑引擎7.30更新说明!

项目介绍 JVS是企业级数字化服务构建的基础脚手架&#xff0c;主要解决企业信息化项目交付难、实施效率低、开发成本高的问题&#xff0c;采用微服务配置化的方式&#xff0c;提供了低代码数据分析物联网的核心能力产品&#xff0c;并构建了协同办公、企业常用的管理工具等&…

Eclipse中导入新项目,右键项目没有Run on Server,Tomcat的add and remove找不到项目

原因分析没有勾选Dynamic Web Module、Java、JavaScriptDynamic Web Module版本问题解决方法Eclipse中右键项目选择Properties左侧点击project facets勾选Dynamic Web Module、Java、JavaScript&#xff0c;注意Dynamic Web Module版本问题,要和tomcat版本对应。- Dynamic Web …

IntelliJ IDEA 2025系列通用软件安装教程(Windows版)

前言 JetBrains系列开发工具&#xff08;如IntelliJ IDEA、PyCharm、WebStorm等&#xff09;是程序员们非常喜爱的集成开发环境。2025年最新版本带来了更多强大的功能和改进。本教程将详细介绍如何在Windows系统上安装JetBrains 2025系列软件。 最近挖到一个宝藏级人工智能学习…

乌鸫科技前端二面

1. 你能给我介绍一下你参与的重要项目&#xff0c;并重点介绍一下做的内容?通俗解释&#xff1a; 挑一个你觉得最拿得出手、技术含量最高的项目&#xff0c;说说这个项目是干什么的&#xff08;比如一个电商网站、一个后台管理系统&#xff09;&#xff0c;你在里面具体负责了…

《c++面向对象入门与实战》笔记

前年的书&#xff0c;翻出来整理一下7章.指针指针 sizeof为4*指针 sizeof为 所指类型的sizeof注意free后置空&#xff0c;避免野指针11章.类

easyExcel生成多个sheet的动态表头的实现

在使用 EasyExcel 实现“多个 Sheet 且每个 Sheet 表头是动态的”需求时&#xff0c;思路如下&#xff1a;✅ 实现思路概述 EasyExcel 的 ExcelWriter 支持多个 Sheet 写入。每个 Sheet&#xff1a; 使用 WriteSheet 创建&#xff1b;可以绑定一个动态生成的表头 List<List&…

SQL 连接类型示例:内连接与外连接

SQL 连接类型示例&#xff1a;内连接与外连接 示例数据表 假设我们有两个表&#xff1a; employees 表:emp_idemp_namedept_id1张三1012李四1023王五1034赵六NULLdepartments 表:dept_iddept_name101销售部102技术部104财务部1. 内连接 (INNER JOIN) 内连接只返回两个表中匹配的…

Ubuntu安装gpu驱动,cuda

系统初始化 1、安装基础软件 apt-get update apt-get -y install openssh-server openssh-client apt-utils freeipmi ipmitool sshpass ethtool zip unzip nano less git netplan.io iputils-ping mtr ipvsadm smartmontools python3-pip socat conntrack libvirt-clients li…

ctfshow_源码压缩包泄露

根据题目信息直接dirsearch解压下来一个.txt文件&#xff0c;一个index.phpflag{flag_here}不对那么就去看index.php也没有东西&#xff0c;于是查看wp发现是访问/fl000g.txt这才是对的还有很多源码泄露需要去了解• git源码泄露• svn源码泄露• DS_Store 文件泄露• 网站备份…

Python 程序设计讲义(54):Python 的函数——函数概述

Python 程序设计讲义&#xff08;54&#xff09;&#xff1a;Python 的函数——函数概述 目录Python 程序设计讲义&#xff08;54&#xff09;&#xff1a;Python 的函数——函数概述一、函数的类型1、内置函数2、自定义函数二、调用函数Python 提供了函数机制&#xff0c;把实…

学习Python中Selenium模块的基本用法(3:下载浏览器驱动续)

前一篇文章主要介绍下载针对火狐浏览器的WebDriver&#xff0c;写那篇文章时才找到能够下最新版本Chrome的WebDriver地址&#xff08;参考文献6&#xff09;&#xff0c;本文继续学习并验证针对Chrome浏览器的WebDriver下载和使用方法。Chrome的WebDriver版本与操作系统相关&am…

AIDL当Parcelable序列化的数据类通信时报“Class not found when unmarshalling“找不到该类时的解决方案

1. 报错栈 &#xff1a;cusText这个类找不到 2 16:01:29.796 1044 5718 E Parcel : Class not found when unmarshalling: com.cus.sdk.cusText 08-02 16:01:29.796 1044 5718 E Parcel : java.lang.ClassNotFoundException: com.cus.sdk.cusText 08-02 16:01:29.796 1…

Django模型查询与性能调优:告别N+1问题

文章目录一、查询基础QuerySet 详解一对多关联查询多对多关联查询二、N1查询问题问题分析检测方法解决方案三、高级查询优化values()values_list()values()和values_list()对比Q() 对象复杂查询查看生成的 SQL四、项目实战场景实战一、查询基础 QuerySet 详解 Django 中通过模…

PyTorch 中 Tensor 统计学函数及相关概念

文章目录PyTorch 中 Tensor 统计学函数及相关概念一、引言二、基础统计学函数&#xff08;一&#xff09;torch.mean()——均值计算&#xff08;二&#xff09;torch.sum()——总和计算&#xff08;三&#xff09;torch.prod()——元素积计算&#xff08;四&#xff09;torch.m…

浅拷贝与深拷贝的区别

浅拷贝和深拷贝是两种不同的对象复制方式&#xff0c;主要区别在于它们如何处理对象内部的引用类型字段。浅拷贝 (Shallow Copy)特点&#xff1a;只复制对象本身&#xff08;基本类型字段&#xff09;和对象中的引用&#xff08;地址&#xff09;不复制引用指向的实际对象原始对…

脚本统计MongoDB集合表数据量

脚本&#xff1a; #!/bin/bashipxxx.xx.xx.xx portxxxx dbxxxdb #user #passwmongo -host ${ip}:${port} <<EOF 2>/dev/null|grep -vE version|not match|session|compressors||Warning|delivers|upcoming|installation|https|switched|bye >collec use ${db}; sho…