react中的useState和useImmer的用法
文章目录
- 一、useState
- 1. 更新基本类型数据
- 2. 更新对象
- 3. 更新嵌套对象
- 4. 更新数组
- 5.更新数组对象
- 二、Immer
- 1. 什么是Immer
- 2. 使用use-immer更新嵌套对象
- 3. 使用useImmer更新数组内部的对象
一、useState
react中文官网教程
1. 更新基本类型数据
在函数式组件中,可以使用 useState 这个 Hook 来定义和管理组件的状态。useState 接受一个初始状态作为参数,并返回一个包含 state 和更新 state 的方法的数组。
下面是一个例子,展示了如何在函数式组件中定义自己的 state:
import React, { useState } from "react";function MyComponent() {// 定义一个名为 count 的 state 变量,并将初始值设置为 0const [count, setCount] = useState(0);const handleClick = () => {setCount(count + 1);};return (<div><p>Count: {count}</p><button onClick={handleClick}>Increase Count</button></div>);
}export default MyComponent;
在上面的例子中,我们使用 useState 创建了一个名为 count 的状态变量,并将其初始值设为 0。然后,我们将 count 的值展示在 <p> 元素中,并在按钮的点击事件中调用 setCount 来更新 count 的值。
每当我们调用 setCount 来更新 count 的值时,React 会重新渲染组件并传递更新后的值给 count。
2. 更新对象
在函数式组件中使用useState更新state中的对象可以通过以下步骤:
- 导入useState:
import React, { useState } from 'react';
- 创建一个对象作为初始状态(state):
const initialState = { name: 'John', age: 30, email: 'john@example.com' };
- 在组件内部使用useState钩子声明state:
const [person, setPerson] = useState(initialState);
这里person是状态对象,setPerson是一个更新状态的函数。useState(initialState)是useState的初始值参数。
- 在组件中使用状态对象:
return (<div><p>Name: {person.name}</p><p>Age: {person.age}</p><p>Email: {person.email}</p></div>
);
- 使用setPerson函数来更新状态对象的值:
const updateEmail = () => {setPerson({ ...person, email: 'updated@example.com' });
};
这里使用了ES6的展开语法(spread syntax)来复制现有的person对象,并更新email属性的值。
- 在组件中调用updateEmail函数来更新状态:
<button onClick={updateEmail}>Update Email</button>
完整的示例代码如下:
import React, { useState } from 'react';const App = () => {const initialState = { name: 'John', age: 30, email: 'john@example.com' };const [person, setPerson] = useState(initialState);const updateEmail = () => {setPerson({ ...person, email: 'updated@example.com' });};return (<div><p>Name: {person.name}</p><p>Age: {person.age}</p><p>Email: {person.email}</p><button onClick={updateEmail}>Update Email</button></div>);
};export default App;
通过点击"Update Email"按钮,可以更新email属性的值为"updated@example.com"。
在React的函数式组件中使用useState来更新一个对象的state时,有几个注意点需要特别注意:
-
useState函数仅仅是按照传入的初始值来初始化state,并不会合并对象。因此,在更新state时,需要先使用解构方式取出旧的对象属性值,然后再设置新的对象属性值。
-
由于useState是按照值的变化来判断是否重新渲染组件的,因此在更新state时,需要保证每一次更新都是返回一个新的对象。直接对原对象进行修改并返回会导致state没有发生变化,从而不会触发重新渲染。
-
可以使用展开运算符扩展现有对象,再进行属性的更改。这样可以确保每次都返回一个新的对象。
为什么要注意这些点呢?
首先,useState是基于值的比较来判断是否重新渲染组件的,如果不注意每次都返回一个新的对象,可能会导致state没有发生变化,从而不会触发重新渲染,无法更新组件视图。
其次,由于函数式组件没有实例的概念,每次组件渲染都是独立的,因此无法像类组件中使用this.setState那样自动合并对象属性。因此,在更新state时,需要手动合并旧的对象属性值和新的对象属性值,以确保不丢失任何旧的state属性。
最后,使用展开运算符扩展对象的方式可以确保每次都返回一个全新的对象,这样可以避免直接对原对象进行修改而导致state没有发生变化的问题。
3. 更新嵌套对象
在React的函数式组件中使用useState更新state中的嵌套对象,可以使用扩展运算符(spread operator)来实现。
举例说明,假设有一个包含嵌套对象的state,如下所示:
const [state, setState] = useState({name: "John",age: 30,address: {street: "123 Main St",city: "New York",state: "NY"}
});
要更新state中的嵌套对象,可以使用useState的setState函数,传递一个新的对象,并使用扩展运算符将原有的state进行展开,再覆盖需要更新的属性。例如,要更新address对象的city属性,可以使用以下代码:
setState({...state,address: {...state.address,city: "Los Angeles"}
});
上面的代码首先使用扩展运算符将原有的state展开,然后再针对需要更新的嵌套对象进行展开,并更新特定的属性。在本例中,我们更新了address对象的city属性,将其值从"New York"更新为"Los Angeles"。
完整的示例代码如下所示:
import React, { useState } from "react";function App() {const [state, setState] = useState({name: "John",age: 30,address: {street: "123 Main St",city: "New York",state: "NY"}});const updateCity = () => {setState({...state,address: {...state.address,city: "Los Angeles"}});};return (<div><p>Name: {state.name}</p><p>Age: {state.age}</p><p>Street: {state.address.street}</p><p>City: {state.address.city}</p><p>State: {state.address.state}</p><button onClick={updateCity}>Update City</button></div>);
}export default App;
在上述示例中,我们首先使用useState定义了包含嵌套对象的state,然后在组件中显示了state中的属性。最后,我们使用一个按钮来调用updateCity函数,该函数会更新state中的嵌套对象。点击按钮后,更新后的city属性值将显示为"Los Angeles"。
4. 更新数组
在函数式组件中使用useState来更新state中的数组,可以通过结构赋值的方式获取数组和更新数组的函数。一般情况下,使用useState更新数组时需要注意以下几点:
-
为了保持state的不可变性,应该使用数组的展开运算符(spread operator)来创建一个新的数组。
-
当更新数组时,需要将要更新的元素和其余元素区分开来。通常使用
Array.map函数来遍历数组,并找到要更新的元素。 -
可以使用
Array.filter函数过滤数组中的元素,从而可以删除特定的元素。 -
注意在更新数组时,在判断相等性时使用严格相等运算符
(===),而不是用“浅比较”运算符(==)。

下面是一个更新数组的例子:
import React, { useState } from "react";function Example() {const [list, setList] = useState([1, 2, 3, 4, 5]);const handleUpdate = () => {// 通过展开运算符创建一个新的数组,并更新数组的第一个元素setList([...list.slice(0, 1), 100, ...list.slice(2)]);};const handleDelete = (item) => {// 使用filter过滤数组,删除特定的元素setList(list.filter((el) => el !== item));};return (<div><ul>{list.map((item) => (<li key={item}>{item}<button onClick={() => handleDelete(item)}>删除</button></li>))}</ul><button onClick={handleUpdate}>更新</button></div>);
}export default Example;
在这个例子中,list是一个包含数字的数组。点击列表项后的“删除”按钮会删除相应的元素,点击“更新”按钮会将数组的第一个元素更新为100。每次更新数组时,使用展开运算符创建一个新的数组,并把更新的元素插入到适当的位置,这样可以保持state的不可变性。在删除元素时,使用filter函数过滤掉特定的元素。
5.更新数组对象
在函数式组件中使用 useState 来更新 state 数组中的内部对象,可以使用解构赋值的方式来获取数组中的指定对象,并使用 useState 来更新该对象的属性。
首先,在函数式组件中使用 useState 定义一个状态变量,可以设置初始状态为一个包含内部对象的数组。例如,使用以下代码定义一个状态变量 items,并设置初始状态为一个包含两个内部对象的数组:
const [items, setItems] = useState([{ name: "apple", quantity: 1 },{ name: "banana", quantity: 2 }
]);
然后,通过解构赋值的方式获取数组中的指定对象,并使用 useState 更新该对象的属性。例如,通过以下代码获取数组中的第一个对象 item 并更新其 name 属性:
const [item, setItem] = useState(items[0]);const handleChangeName = () => {setItem(prevItem => ({ ...prevItem, name: "orange" }));
};
在上述代码中,setItem 函数通过接收一个回调函数来更新 item 对象的属性。该回调函数使用展开运算符 ... 将之前的属性拷贝到一个新的对象中,并更新 name 属性为 "orange"。
需要注意的是,在更新数组时,不能直接修改数组中的对象。要更新数组中的对象,需要先拷贝数组,然后再更新其中的对象。可以使用 Array.map() 方法来拷贝数组并更新其中的对象。例如,使用以下代码将数组中的第一个对象的 name 属性改为 "orange":
const updatedItems = items.map((item, index) => {if (index === 0) {return { ...item, name: "orange" };}return item;
});setItems(updatedItems);
上述代码中,Array.map() 方法遍历数组,当索引为 0 的时候,返回一个新的对象,并更新其 name 属性为 "orange";否则,返回原来的对象。然后,将得到的新数组 updatedItems 设置为新的状态值。
二、Immer
1. 什么是Immer
Immer是一个JavaScript库,用于管理不可变状态。它通过基于原始状态创建一个新的状态树来实现不可变性。它提供了一种简单而直观的方式来处理复杂的状态更新逻辑,使代码更易于理解和维护。Immer可以与任何JavaScript框架或库一起使用,并且对于处理大型数据结构和深层嵌套的对象非常有用。它是一个流行的工具,用于管理JavaScript应用程序中的状态。
在React的函数式组件中,推荐使用Immer来处理复杂的数据state,特别是深层嵌套的数组对象,有以下几个原因:
-
操作不可变数据更加方便:Immer提供了一种简洁的方式来直接对不可变数据进行修改。通过使用Immer的produce函数,可以在不直接修改原始数据的情况下,创建一个可变的草稿副本,并且在草稿上进行所有的更改操作。这样可以避免因为直接修改原始数据而导致的引用问题和副作用问题。
-
性能优化:Immer采用了一种优化技术,称为“结构共享”,它能够在不实际复制数据的情况下实现不可变数据的修改。这种技术可以大大提高性能,特别是对于大型、深层嵌套的数据结构。
-
减少模板嵌套深度:在处理深层嵌套的数组对象时,使用Immer可以减少模板嵌套的深度。因为Immer提供了一种更简洁、更直观的方式来修改深层嵌套的数据,这能够提高代码的可读性和可维护性。
总之,使用Immer可以帮助我们更方便地处理复杂的数据state,尤其是深层嵌套的数组对象。它简化了对不可变数据的操作,并提供了性能优化的机制,使得我们能够更加高效地开发React应用。
2. 使用use-immer更新嵌套对象
在React的函数式组件中,可以使用use-immer库来更新嵌套对象的state。use-immer是基于Immer库的React Hook封装,它可以方便地进行不可变状态更新。
以下是一个示例,展示如何使用use-immer更新嵌套对象的state:
首先,安装use-immer库:
npm install use-immer
然后,导入useImmer函数并在函数式组件中使用它:
import React, { useState } from 'react';
import { useImmer } from 'use-immer';function App() {const [state, setState] = useImmer({user: {name: 'John',age: 25}});// 更新嵌套对象的示例函数const updateUserName = () => {setState(draft => {draft.user.name = 'Tom';});};return (<div><h1>{state.user.name}</h1><p>{state.user.age}</p><button onClick={updateUserName}>Update Name</button></div>);
}export default App;
在上面的示例中,我们使用useState和useImmer来声明和初始化state。state是一个嵌套对象,包含一个user对象。然后,我们在updateUserName函数中使用setState函数来更新嵌套对象的state。在setState的回调函数中,我们可以使用draft参数来修改状态。
使用use-immer库的好处是,我们可以在回调函数中直接修改draft对象,就像在原始状态上进行修改一样。useImmer会负责处理不可变性,最终生成一个新的状态,并将其应用于组件。
以上是使用use-immer库在React的函数式组件中更新嵌套对象状态的示例。可以根据实际需求进行相应的修改和扩展。
3. 使用useImmer更新数组内部的对象
可以使用useImmer hook结合immer来更新函数式组件中数组内部的对象。useImmer hook是在React中使用Immer库的推荐方式。
以下是一个示例,说明如何使用useImmer更新数组内部的对象:
import React, { useState } from 'react';
import { useImmer } from 'use-immer';const MyComponent = () => {// 使用useState hook创建数组状态const [data, setData] = useState([{ id: 1, name: 'John' },{ id: 2, name: 'Jane' },{ id: 3, name: 'Bob' },]);// 使用useImmer hook创建可变的数据副本draft和更新函数setDraftconst [draft, setDraft] = useImmer(data);// 更新对象的名称const updateName = (id, name) => {setDraft(draft => {// 使用immer的produce函数来更新draftconst item = draft.find(item => item.id === id);if (item) {item.name = name;}});};return (<div>{draft.map(item => (<div key={item.id}><span>{item.name}</span>{/* 在组件中触发更新名称的函数 */}<button onClick={() => updateName(item.id, 'Updated Name')}>Update Name</button></div>))}</div>);
};export default MyComponent;
在上面的代码中,我们首先使用useState hook创建了一个数组状态data,然后使用useImmer hook创建了一个可变的数据副本draft和一个更新函数setDraft。在更新函数中,我们使用immer的produce函数来更新draft,修改特定ID对应的对象的名称。
在组件的返回值中,我们使用draft.map循环遍历数组内部的对象,显示每个对象的名称,并使用按钮来触发更新的函数。
当你点击"Update Name"按钮时,会更新draft中特定ID对应的对象的名称,由于draft是可变的,所以React会自动更新组件的渲染。
相关文章:
react中的useState和useImmer的用法
文章目录 一、useState1. 更新基本类型数据2. 更新对象3. 更新嵌套对象4. 更新数组5.更新数组对象 二、Immer1. 什么是Immer2. 使用use-immer更新嵌套对象3. 使用useImmer更新数组内部的对象 一、useState react中文官网教程 1. 更新基本类型数据 在函数式组件中,…...
Can‘t compile code “launch: program <program_path> does not exist “
StackOverflow上有一个类似的提问 我的情况很特殊,上面的回答没有解决我的问题,最后我发现是我的cpp文件名称为数字开头(类似于1_floy.cpp),把名字里的数字挪到后面就好了。。。。。...
Mac电脑上升级nodejs
第一步,先查看本机node.js版本: node -v 第二步,清除node.js的cache: sudo npm cache clean -f 第三步,安装 n 工具,这个工具是专门用来管理node.js版本的,别怀疑这个工具的名字,…...
基于单片机的太阳跟踪系统的设计
欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 技术交流认准下方 CSDN 官方提供的联系方式 文章目录 概要 一、设计的主要内容二、硬件电路设计2.1跟踪控制方案的选择2.1.1跟踪系统坐标系的选择2.2系统总体设计及相关硬件介绍…...
V3Det大规模词汇视觉检测数据集与LaRS海上全景障碍物检测数据集
V3Det与LaRS是ICCV2023上发表的数据集工作,规模都比较大,后续有可能会用到,因此记录下来。 V3Det: Vast Vocabulary Visual Detection Dataset Paper: https://arxiv.org/abs/2304.03752 URL: https://v3det.openxlab.org.cn/ 在现实世界中…...
ubuntu(18.04) 安装 blast
1、下载 https://ftp.ncbi.nlm.nih.gov/blast/executables/blast/LATEST/2、解压,配置环境变量 tar zvxf ncbi-blast-2.14.1-x64-linux.tar.gz解压后改名为 blast 配置环境变量,可以不配置 使用的时候直接绝对路径使用 vim ~/.bashrc 将下面添加道最…...
3.2每日一题(定积分求抽水做工问题)
1、画图,把题目的容器画出来:球形容器,半径为R 2、根据容器的形状进行分析: 抽水的实质是不同深度的水抽出去走的位移是不一样的>抽水的过程 : (1)先考虑深度为 x到xdx 的薄层水抽出去做多少…...
c语言基础:L1-063 吃鱼还是吃肉
国家给出了 8 岁男宝宝的标准身高为 130 厘米、标准体重为 27 公斤;8 岁女宝宝的标准身高为 129 厘米、标准体重为 25 公斤。 现在你要根据小宝宝的身高体重,给出补充营养的建议。 输入格式: 输入在第一行给出一个不超过 10 的正整数 N&am…...
<if> 标签中使用了不正确的语法。在 XML 中,<if> 标签不需要使用 <![CDATA[ ... ]]> 将条件语句包装起来。 否则会报错
标签中使用了不正确的语法。在 XML 中, 标签不需要使用 <![CDATA[ ... ]]> 将条件语句包装起来。 否则会报错...
Ubuntu 诞生 19 年
导读2004 年 10 月 20 日,Ubuntu 4.10 正式发布,代号‘Warty Warthog’。 作为 Ubuntu 第一个版本,4.10 问世后立刻受到广大 Linux 用户欢迎。它搭载了当时最新的 GNOME 2.8 桌面环境,以及一系列实用软件,比如 Mozilla…...
JVM进阶(3)
一)什么是垃圾? 垃圾指的是在应用程序中没有任何指针指向的对象,这个对象就是需要被回收的垃圾,如果不及时的针对内存中的垃圾进行清理,那么这些垃圾对象所占用的内存空间可能一直保留到应用程序结束,被保留的空间无法…...
Qt QWidget、QDialog、QMainWindow的区别
QWidget QWidget是Qt框架中最基础的窗口类,可以理解为用户界面的最基本单元。QWidget类提供了一个空白窗口,可以通过继承该类来创建自定义的窗口类。QWidget类提供了基本的窗口属性和方法,如大小、位置、标题、图标等。 QDialog QDialog是…...
软考 系统架构设计师系列知识点之设计模式(10)
接前一篇文章:软考 系统架构设计师系列知识点之设计模式(9) 所属章节: 老版(第一版)教材 第7章. 设计模式 第2节. 设计模式实例 相关试题 9. 某软件公司欲设计一款图像处理软件,帮助用户对拍…...
【python爬虫】设计自己的爬虫 1. request封装
通过requests.session().request 封装request方法 考虑到请求HTTP/2.0 同时封装httpx 来处理HTTP/2.0的请求 封装requests # 遇到请求失败的情况时 重新请求,请求5次等待2s retry(stop_max_attempt_number5, retry_on_resultlambda re_data: re_data is None, wai…...
8.0 新特性 - innodb_ddl_threads
前言 MySQL 8.0.27 引入了一个新变量来控制 InnoDB 可用于创建(排序和构建)二级索引的最大并行线程数:innodb_ddl_threads 通过调整该参数,可以提升二级索引的创建速度。 参数介绍 1. innodb_ddl_threads 创建二级索引时&…...
pgAdmin 4 v7.8 发布,PostgreSQL 开源图形化管理工具
导读pgAdmin 是 PostgreSQL 领先的开源图形化管理工具。pgAdmin 4 旨在满足新手和有经验的 Postgres 用户的需求,提供强大的图形界面,简化了数据库对象的创建、维护和使用。 pgAdmin 开发团队日前发布了 pgAdmin 4 v7.8 版本,这个版本包括 21…...
Realrek 2.5G交换机 8+1万兆光RTL8373-VB-CG方案简介
新一代2.5G交换机方案RTL8373-VB-CG可以提供4中不同形态 a. 52.5G 电口110G光》RTL8373 b. 52.5G 电口110G电》RTL83738261 c. 82.5G 电口110G光》RTL83738224 d.82.5G 电口110G电口》RTL837382248261 1.概述 Realtek RTL8373-CG是一款低功耗、高性能、高度集成的八端口2.5G和一…...
Linux命令随笔
文章目录 grep命令 grep命令 例如,在Linux系统中,要在当前目录下的所有文件中搜索包含字符串"hello"的行,并显示文件名,可以使用以下命令: grep -r "hello" .其中,"."表示…...
最新版scene-builder安装
JavaFX Scene Builder是一个可视化的布局工具,用于设计JavaFX用户界面。它允许开发人员使用拖放和可视化的方式创建和编辑JavaFX界面,而无需直接编写代码。 JavaFX Scene Builder提供了一个直观的界面,让开发人员可以轻松地创建和修改JavaFX…...
直击电商商城内核!一站式解决方案
作为一家深耕电商运营多年的软件开发公司,我们拥有先进的轻量级电商中台系统,且100%开源,包含B2C、B2B2C、S2B2C、O2O和社区团购等多种商业模式,无论在技术、业务架构、功能、设计还是售后支持上,我们都秉承着追求极致…...
wordpress后台更新后 前端没变化的解决方法
使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…...
Vim 调用外部命令学习笔记
Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...
云计算——弹性云计算器(ECS)
弹性云服务器:ECS 概述 云计算重构了ICT系统,云计算平台厂商推出使得厂家能够主要关注应用管理而非平台管理的云平台,包含如下主要概念。 ECS(Elastic Cloud Server):即弹性云服务器,是云计算…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Opencv中的addweighted函数
一.addweighted函数作用 addweighted()是OpenCV库中用于图像处理的函数,主要功能是将两个输入图像(尺寸和类型相同)按照指定的权重进行加权叠加(图像融合),并添加一个标量值&#x…...
SpringBoot+uniapp 的 Champion 俱乐部微信小程序设计与实现,论文初版实现
摘要 本论文旨在设计并实现基于 SpringBoot 和 uniapp 的 Champion 俱乐部微信小程序,以满足俱乐部线上活动推广、会员管理、社交互动等需求。通过 SpringBoot 搭建后端服务,提供稳定高效的数据处理与业务逻辑支持;利用 uniapp 实现跨平台前…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
