React的State和setState

如何确地使用 State

  1. 不要直接修改 State.
  2. 修改State应该使用 setState():
  3. 构造函数是唯一可以给 this.state 赋值的地方

State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件

我们可以在我们的自定义组件中添加私有的State
jcode

class Clock extends React.Component {constructor(props) {   super(props);    this.state = {date: new Date()}; }render() {return (<div><h1>Hello, world!</h1><h2>It is {this.state.date.toLocaleTimeString()}.</h2>      </div>);}
}const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Clock />);

那我们如果要更新State要怎么去更新呢?

答案就是setState,而且必须使用setState。

为什么必须使用setState呢?

在开发中我们并不能直接通过修改state的值来让界面发生更新。因为我们修改了state之后,是希望React根据最新的State来重新渲染界面,但是这种方式的修改React并不知道数据发生了变化。React并没有实现类似于Vue2中的Object.defineProperty或者Vue3中的Proxy的方式来监听数据的变化;所以我们必须通过setState来告知React数据已经发生了变化。React才会更新我们对应的数据以及更新页面的渲染

那你可能会问:为什么我们在组件中并没有实现setState的方法,为什么可以调用呢?

原因很简单,setState方法是从Component中继承过来的。

关于setState的源码截图:

image.png

setState的更新是异步的!

异步的情况

我们做个实验,我们在更新完的函数里面直接console.log我们的state的值。可以发现我们的setState是异步的

代码:

jcode

import React, { useState, useCallback } from 'react';
import ReactDom from 'react-dom';class Test extends React.Component {constructor(props) {   super(props);    this.state = {message:"我是初始的State"  }; this.handleClick = this.handleClick.bind(this)}handleClick(){this.setState({message:"我是更新后的结果" },);console.log(this.state.message)} render() {return (<button onClick={this.handleClick}>更新</button>);}
}
ReactDom.render(<Test />, document.getElementById('app'));

点击按钮之后的打印结果,

image.png

我们可以很明显的发现打印出来的值仍然是旧值,未修改前的值。所以虽然我们setState修改了我们的State值,迪但是在代码运作中,他并不是立即同步去进行修改的。

为什么setState设计为异步呢?

  • setState设计为异步,可以显著的提升性能

    • 如果每次调用 setState都进行一次更新,那么意味着render函数会被频繁调用,界面重新渲染,这样效率是很低的。
    • 最好的办法应该是获取到多个更新,之后进行批量更新。
  • 如果同步更新了state,但是还没有执行render函数,那么state和props不能保持同步

    • state和props不能保持一致性,会在开发中产生很多的问题。

github上也有很多讨论,React核心成员(Redux的作者)Dan Abramov也有对应的回复GitHub回应链接这里就不细究了。

如何获取异步的结果

方式一:setState的回调

  • setState接受两个参数:第二个参数是一个回调函数,这个回调函数会在更新后会执行;
  • 格式如下:setState(partialState, callback)

jcode

import React, { useState, useCallback } from 'react';
import ReactDom from 'react-dom';class Test extends React.Component {constructor(props) {   super(props);    this.state = {message:"我是初始的State"  }; this.handleClick = this.handleClick.bind(this)}handleClick(){this.setState({message:"我是更新后的结果" },() =>{//拿到的是更新后的值console.log(this.state.message)});//拿到的是更新前的值console.log(this.state.message)} render() {return (<button onClick={this.handleClick}>更新</button>);}
}ReactDom.render(<Test />, document.getElementById('app'));

点击按钮之后的打印结果:

image.png

可以看见我们拿到的值是最新的值

方式二: componentDidUpdate()生命周期函数

jcode

import React, { useState, useCallback } from 'react';
import ReactDom from 'react-dom';class Test extends React.Component {constructor(props) {   super(props);    this.state = {message:"我是初始的State"  }; this.handleClick = this.handleClick.bind(this)}handleClick(){this.setState({message:"我是更新后的结果" });} componentDidUpdate(){console.log(this.state.message)}render() {return (<button onClick={this.handleClick}>更新</button>);}
}ReactDom.render(<Test />, document.getElementById('app'));

点击按钮之后的打印结果:

image.png

可以看见我们拿到的值是最新的值

方式三:箭头函数

因为 this.props 和 this.state 可能会异步更新,所以我们不能依赖他们的值来更新下一个状态。可能会出现bug

那如果我们一定要同时使用this.propsthis.state来更新我们的数据怎么办呢?

可以让 setState() 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 作为第二个参数

class MyComponent extends React.Component {constructor(props) {super(props);this.state = {counter: 0};}handleClick = () => {this.setState((prevState, props) => {return {counter: prevState.counter + props.increment};});};render() {return (<div><p>Counter: {this.state.counter}</p><button onClick={this.handleClick}>Increment</button></div>);}
}

非异步的情况

setState一定是异步吗?

其实分成两种情况:

  1. 在组件生命周期或React合成事件中,setState是异步
  2. 在setTimeout或者原生dom事件中,setState是同步

在setTimeout中更新

我们可以做个试验,在setTimeout里面进行更新,更新完之后直接打印我们的state值。可以看见我们的setState是同步的

import React, { useState, useCallback } from 'react';
import ReactDom from 'react-dom';class Test extends React.Component {constructor(props) {   super(props);    this.state = {message:"我是初始的State"  }; this.handleClick = this.handleClick.bind(this)}handleClick(){setTimeout(() =>{this.setState({message:"我是更新后的结果"})// 更新后里面打印结果console.log(this.state.message)},0)}render() {return (<button onClick={this.handleClick}>更新</button>);}
}ReactDom.render(<Test />, document.getElementById('app'));

点击按钮之后的打印结果:

image.png

可以看见我们拿到的值是最新的值

在原生DOM事件中更新

我们可以做个试验,在原生DOM事件中进行更新,更新完之后直接打印我们的state值。可以看见我们的setState是同步的

componentDidMount(){const btnEl =document.getElementById("btn");btnEl.addEventListener('click',() =>{this.setState({message:"我是更新后的结果"})console.loh(this.state.message)})
}

点击按钮之后的打印结果:

image.png

可以看见我们拿到的是最新的值而且是同步更新的

State 的更新会被合并

在 React 中,setState() 的更新操作可能会被合并。当你多次调用 setState() 时,React 可能会将多个更新操作合并为单个更新,以提高性能。

      componentDidMount() {fetchPosts().then(response => {this.setState({posts: response.posts      });});fetchComments().then(response => {this.setState({comments: response.comments      });});}

当我们的posts被更新之后comments还是旧的值,comments在更新完成后两个值才是最新的

注意: React状态更新的合并是基于浅合并(shallow merge)的方式。如果状态对象中包含嵌套的对象或数组,并且你希望进行深层次的合并,需要自行处理合并逻辑。

什么是浅合并?

首先先给大家用代码介绍什么是浅合并

class MyComponent extends React.Component {constructor(props) {super(props);this.state = {person: {name: 'John',age: 25}};}handleClick = () => {this.setState({person: {name: 'Jane'}});};render() {return (<div><p>Name: {this.state.person.name}</p><p>Age: {this.state.person.age}</p><button onClick={this.handleClick}>Update</button></div>);}
}

上述示例中,我们的状态对象 person 包含了 name 和 age 属性。当点击按钮时,我们调用 setState() 更新状态中 person 对象的 name 属性。

由于状态更新是基于浅合并的方式,React 会用新的 person 对象替换当前状态中的 person 对象,导致 age 属性丢失。因此,在重新渲染时,person 对象只包含 name 属性。

如何进行深层次的合并?

如果你希望进行深层次的合并,需要自行处理合并逻辑。可以使用深拷贝方法(如 Object.assign()、spread 运算符或第三方库)来创建一个新的深层次对象,并将其与当前状态进行合并。

代码示例:

import React from 'react';
//要使用 cloneDeep(),你需要安装 lodash 库并导入相应的方法。
import cloneDeep from 'lodash/cloneDeep';class MyComponent extends React.Component {constructor(props) {super(props);this.state = {person: {name: 'John',age: 25}};}handleClick = () => {const updatedPerson = cloneDeep(this.state.person);updatedPerson.name = 'Jane';this.setState({person: updatedPerson});};render() {return (<div><p>Name: {this.state.person.name}</p><p>Age: {this.state.person.age}</p><button onClick={this.handleClick}>Update</button></div>);}
}

在上述示例中,我们使用 cloneDeep() 方法从当前状态中创建了一个深层次的副本 updatedPerson,然后修改了副本的 name 属性。最后,我们使用新的副本来更新状态中的 person 对象,实现了深层次的合并。

React要求我们遵从“数据向下流动规则”

  • 组件间的state都是独立的互不干扰的,他们之间的状态都是互不知晓的

  • 我们的父组件的数据更新可以影响我的的子组件,但是子组件的数据更新不能往上传给父组件,并且不能影响父组件。

  • 组件可以选择把它的 state 作为 props 向下传递到它的子组件中,但是组件本身无法知道它是来自于父组件的state还是props

我们如何判断该数据是否应该放入state

通过问自己以下三个问题,你可以逐个检查相应数据是否属于 state:

  • 该数据是否是由父组件通过 props 传递而来的?如果是,那它应该不是 state。
  • 该数据是否随时间的推移而保持不变?如果是,那它应该也不是 state。
  • 你能否根据其他 state 或 props 计算出该数据的值?如果是,那它也不是 state。

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

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

相关文章

虚拟机拖拽文档造成缓存过大

查看文件夹大小&#xff1a;du -h --max-depth1 缓存位置&#xff1a;~/.cache/vmware/drag_and_drop 删除&#xff1a;rm -fr ~/.cache/vmware/drag_and_drop 释放了3GB

CVPR上新 | 从新视角合成、视频编解码器、人体姿态估计,到文本布局分析,微软亚洲研究院精选论文

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本周&#xff0…

python如何判断图片是否为空

如下所示&#xff1a; import cv2im cv2.imread(2.jpg) if im is None:print("图像为空") # cv2.imshow("ss", im) # cv2.waitKey(0)

编码规则UTF-8 和 UTF-16的区别

UTF-8 和 UTF-16 的设计背景与历史 为了更好地理解 UTF-8 和 UTF-16 的设计选择和背景&#xff0c;以下是两种编码方案的历史、设计动机和它们在计算机科学中的应用。 Unicode 的背景 在 Unicode 之前&#xff0c;不同的字符集和编码方案使得跨平台和国际化的文本处理变得复…

2024年AI+游戏赛道的公司和工具归类总结

随着人工智能技术的飞速发展,AI在游戏开发领域的应用越来越广泛。以下是对2024年AI+游戏赛道的公司和工具的归类总结,涵盖了从角色和场景设计到音频制作,再到动作捕捉和动画生成等多个方面。 2D与3D创作 2D创作工具:专注于角色和场景的平面设计,提供AI辅助的图案生成和风…

【2024德国工作】外国人在德国找工作是什么体验?

挺难的&#xff0c;德语应该是所有中国人的难点。大部分中国人进德国公司要么是做中国业务相关&#xff0c;要么是做技术领域的工程师。先讲讲人在中国怎么找德国的工作&#xff0c;顺便延申下&#xff0c;德国工作的真实体验&#xff0c;最后聊聊在今年的德国工作签证申请条件…

【八股系列】说一下mobx和redux有什么区别?(React)

&#x1f389; 博客主页&#xff1a;【剑九 六千里-CSDN博客】 &#x1f3a8; 上一篇文章&#xff1a;【介绍React高阶组件&#xff0c;适用于什么场景&#xff1f;】 &#x1f3a0; 系列专栏&#xff1a;【面试题-八股系列】 &#x1f496; 感谢大家点赞&#x1f44d;收藏⭐评…

双例集合(二)——双例集合的实现类之HashMap容器类

双例集合的常用实现类有HashMap和TreeMap两个&#xff0c;通过这两个类我们可以实现Map接口定义的容器&#xff0c;一般情况下使用HashMap容器类较多。 HashMap容器类是Map接口最常用的实现类&#xff0c;它的底层采用Hash算法来实现&#xff0c;这也就满足了键key不能重复的要…

揭秘!速卖通、敦煌网、国际站出单背后的黑科技:自养号测评技术

在竞争激烈的跨境电商平台上&#xff0c;如亚马逊、速卖通、Lazada、Shopee、敦煌网、Temu、Shein、美客多和阿里国际等&#xff0c;稳定出单成为每位卖家共同追求的目标。为了实现这一目标&#xff0c;卖家需要从产品选择、运营策略和客户服务等多个维度进行全面考量&#xff…

华为重磅官宣:超9亿台、5000个头部应用已加入鸿蒙生态!人形机器人现身 专注AI芯片!英伟达挑战者Cerebras要上市了

内容提要 华为表示&#xff0c;盘古大模型5.0加持&#xff0c;小艺能力全新升级。小艺智能体与导航条融为一体&#xff0c;无处不在&#xff0c;随时召唤。只需将文字、图片、文档“投喂”小艺&#xff0c;即可便捷高效处理文字、识别图像、分析文档。 正文 据华为终端官方微…

JavaWeb阶段学习知识点(一)

【参考视频】https://www.bilibili.com/video/BV1m84y1w7Tb?p=167&vd_source=38a16daddd38b4b4d4536e9c389e197f SpringBoot项目的创建和接口配置 做一个springboot项目,从创建项目到实现浏览器访问localhost:8080/hello返回字符串hello world的全流程 1. 创建项目 idea新…

上海科技博物馆超薄OLED柔性壁纸屏应用方案

产品&#xff1a;2组55寸OLED柔性屏2x1 特点&#xff1a;嵌入墙体&#xff0c;与空间装饰融入一体 用途&#xff1a;播放文物展示 一、项目背景 上海科技博物馆作为展示科技与文化的交汇点&#xff0c;一直致力于为观众提供沉浸式的参观体验。为了提升文物展示的现代化和科技感…

芯片验证分享系列总结及PPT分享

大家好&#xff0c;我是谷公子。花了将近两个月时间&#xff0c;《芯片验证分享》这一系列视频分享已经更新完了&#xff0c;内容涵盖了名词解释、芯片验证原则、激励开发、代码审查以及芯片调试。这一系列视频主要侧重于芯片验证理论的分享&#xff0c;希望可以帮助大家构建芯…

wvp-GB28181-pro 源码分析-点播流程(三)

文章目录 一 、28181-2016标准文档中的点播流程二 、点播流程源码分析2.1 页面发起点播请求2.2 与ZLM协商SSRC信息2.3 订阅zlmediakit的hook消息及发送invite信令2.4 处理invite信令响应并应答2.5 收到ZLM的推流通知2.6 播放成功2.7 停止点播流程2024年6月20日下载的wvp-GB2818…

程序员·职场效能必修宝典㊿:正视自己的职业倦怠

> 【易编橙终身成长社群,相遇已是上上签!】- 点击跳转~ < 作者:哈哥撩编程 (视频号同名) 图书作者:程序员职场效能宝典 博客专家:全国博客之星第四名 超级个体:COC上海社区主理人 特约讲师:谷歌亚马逊分享嘉宾

VBA语言専攻T3学员领取资料通知

各位学员∶本周MF系列VBA技术资料增加631-635讲&#xff0c;T3学员看到通知后请免费领取,领取时间6月21日晚上19:00-6月22日晚上20:00。本次增加内容&#xff1a; MF631:提取某列数据的唯一值 MF632:自动调整文本并旋转到90度 MF633:仅复制格式 MF634:Mod运算判断奇数偶数 …

又发现一款独立清理神器,界面清爽,功能强大,没有广告!

360清理Pro独立提取版是360公司推出的一款清理软件&#xff0c;主要用于清理系统垃圾和优化系统性能&#xff0c;涵盖了四大类型的清理场景&#xff0c;分别为&#xff1a;微信、QQ的垃圾扫描及清理&#xff0c;系统盘中的大文件、重复文件扫描及清理以及系统软件使用痕迹的清理…

毕业答辩制作PPT【攻略】

毕业答辩制作PPT【攻略】 前言版权毕业答辩制作PPT【攻略】一、WPS AI 15天免费会员二、AI文档生成PPT三、修改完善PPT 最后 前言 2024-06-14 23:43:05 以下内容源自《【攻略】》 仅供学习交流使用 版权 禁止其他平台发布时删除以下此话 本文首次发布于CSDN平台 作者是CSDN…

fast lio 运行mid360采集的数据,并保存每一帧的点云PCD和位姿

首先我们看到在map_incremental中存在一个保存每一帧PCD文件的代码&#xff0c;因此想利用改代码。 如何修改呢&#xff1f; 一. 改每一帧无畸变点云的PCD的保存代码 /**************** save map ****************//* 1. make sure you have enough memories/* 2. noted that…

数字化物资管理系统的未来:RFID技术的创新应用

在信息化和智能化不断发展的背景下&#xff0c;物资管理系统的数字化转型已成为各行各业关注的焦点。RFID技术作为一种先进的物联网技术&#xff0c;通过全面数字化实现物资信息的实时追踪和高效管理&#xff0c;为企业的物资管理提供了强有力的支持。 首先&#xff0c;RFID技…