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。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

web-原生Ajax

概念: Asynchronous JavaScript And XML&#xff0c;异步的JavaScript和XML。 作用: 数据交换:通过Ajax可以给服务器发送请求&#xff0c;并获取服务器响应的数据。 异步交互:可以在不重新加载整个页面的情况下&#xff0c;与服务器交换数据并更新部分网页的技术&#xff0c;如…

Introduction to linear optimization 第 2 章课后题答案 11-15

线性规划导论 Introduction to linear optimization (Dimitris Bertsimas and John N. Tsitsiklis, Athena Scientific, 1997)&#xff0c; 这本书的课后题答案我整理成了一个 Jupyter book&#xff0c;发布在网址&#xff1a; https://robinchen121.github.io/manual-introdu…

Kafka第一篇——内部组件概念架构启动服务器zookeeper选举以及底层原理

目录 引入 ——为什么分布式系统需要用第三方软件&#xff1f; JMS 对比 组件 架构推演——备份实现安全可靠 &#xff0c; Zookeeper controller的选举 controller和broker底层通信原理 BROKER内部组件 ​编辑 topic创建 引入 ——为什么分布式系统需要用第三方软件&#…

游戏本地化以拓展海外市场

Logrus IT Korea的总监元庆燕&#xff08;KyoungYeon Won&#xff09;发表了一场关于“游戏本地化”的讲座&#xff0c;讲述了独立游戏开发者如何在梦想拓展海外市场的过程中&#xff0c;正确地本地化他们的游戏以满足国际市场的期望&#xff0c;以及实现这一重要任务的过程。 …

Java——包

一、包 1、简要介绍 在Java编程语言中&#xff0c;包&#xff08;Package&#xff09; 是一种用来组织和管理类&#xff08;Class&#xff09;和接口&#xff08;Interface&#xff09;的机制。包为开发者提供了一种逻辑分组的方式&#xff0c;使代码更加模块化、结构化和易于…

k8s部署wordpress及性能优化

镜像版本&#xff1a;wordpress mysql版本&#xff1a;mysql:8.0.27 部署wordpress&#xff1a;v1 此版本包含wordpress基础服务&#xff0c;可访问&#xff0c;但是一旦pod重新创建会丢失数据&#xff0c;文章中的图片等也会丢失&#xff0c;且只又一个pod&#xff0c;性能…

基于PSO粒子群优化的CNN-GRU的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 卷积神经网络&#xff08;CNN&#xff09; 4.2 CNN-GRU模型架构 4.3 CNN-GRU结合PSO的时间序列预测 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软…

20240622 每日AI必读资讯

&#x1f916;力压GPT-4o&#xff01;新王Claude 3.5 Sonnet来了&#xff0c;直接免费可用 - 新模型在推理、知识和编码能力评估方面超越了以前的版本和竞争对手GPT 4o模型&#xff0c;同时其运行速度是Claude 3 Opus的两倍。 - 该模型可在http://Claude.ai和Claude iOS应用上…

牛客练习题打卡--redis

A list保证数据线性有序且元素可重复&#xff0c;它支持lpush、blpush、rpop、brpop等操作&#xff0c;可以当作简单的消息队列使用&#xff0c;一个list最多可以存储2^32-1个元素; redis中set是无序且不重复的; zset可以按照分数进行排序 &#xff0c;是有序不重复的; Redi…

5步快速了解电商渠道数字化管理||电商API数据采集|数据分析

随着电商平台的飞速发展&#xff0c;电商渠道占据品牌经销渠道的比重越来越大&#xff0c;以前只有线下经销渠道的时代已经结束&#xff0c;但是随着渠道的拓展&#xff0c;其中出现了很多问题&#xff0c;如线上渠道或者店铺数量更大、扰乱秩序成本更低、日常上线和下线变动价…

『FPGA通信接口』LVDS接口(4)LVDS接收端设计

文章目录 1.LVDS接收端概述2逻辑框图3.xapp855训练代码解读4.接收端发送端联调5.传送门 1.LVDS接收端概述 接收端的传输模型各个属性应该与LVDS发送端各属性一致&#xff0c;例如&#xff0c;如果用于接收CMOS图像传感器的图像数据&#xff0c;则接收端程序的串化因子、通道个…

ardupilot开发 --- Jetson Orin Nano 后篇

我拼命加速&#xff0c;但贫穷始终快我一步 0~1920. visp-d455&#xff1a;基于IBVS的Pixhawk无人机视觉伺服20.1 基础关于连接、通讯、UDP forward服务&#xff1a;一些相关的、有用的例程Linux C程序的gdb断点调试搭建仿真解决【testPixhawkDroneTakeoff.cpp例程能解锁但起飞…

unity-调用讯飞星火语音唤醒-新版windowsSDK

调用讯飞星火语音唤醒-新版windowsSDK 先贴一张在unity中 wins系统下成功调用新版的讯飞windowsSDK的运行截图 为什么要用讯飞的语音唤醒&#xff1f; 项目中需要在unity和win系统下进行语音唤醒开启语音对话&#xff0c;而语音唤醒比较成熟的方案大多都是在linux系统下的&…

vue实现的商品列表网页

一、商品列表效果如下 二、代码&#xff1b; vue实现的商品列表网页 &#xff0c; 图片在vue项目的Public文件夹里的 imgs中 <template><div class"common-layout"><!-- el-container:外层容器。 当子元素中包含 <el-header> 或 <el-foo…

【性能优化】表分桶实践最佳案例

分桶背景 随着企业的数据不断增长&#xff0c;数据的分布和访问模式变得越来越复杂。我们前面介绍了如何通过对表进行分区来提高查询效率&#xff0c;但对于某些特定的查询模式&#xff0c;特别是需要频繁地进行数据联接查或取样的场景&#xff0c;仍然可能面临性能瓶颈。此外…

Vitis Accelerated Libraries 学习笔记--OpenCV 运行测试

目录 1. 简介 2. 实例测试 2.1 实例介绍 2.1 创建工程 3 常见错误 3.1 核心共享库报错 4. 总结 1. 简介 在《Vitis Accelerated Libraries 学习笔记--OpenCV 安装指南-CSDN博客》一文中&#xff0c;我详尽地介绍了 OpenCV 的安装过程。尽管 Vitis Vision 库的实现本身并…

泽众云真机-平台即将升级支持华为机型HarmonyOS NEXT系统

具小编了解&#xff0c;泽众云真机即将升级支持华为机型HarmonyOS NEXT系统。有些人可能对HarmonyOS NEXT系统了解不多。 之前我们有个银行项目&#xff0c;客户要求测试华为HarmonyOS NEXT系统环境&#xff0c;当时我们云真机尚未有该系统的机型&#xff0c;然后技术人员向华为…

企业智慧办公管理平台

摘要 在之前的疫情中&#xff0c;大多数企业都受到了较大的冲击&#xff0c;然而一些公司却因为工作的特殊性可以居家远程办公&#xff0c;不过这些企业在管理员工的过程中却遇到了较大的困难&#xff0c;这是因为这些企业的管理系统根本大多都无法管理员工的工作项目&#xf…

【面试干货】 Java 中的 HashSet 底层实现

【面试干货】 Java 中的 HashSet 底层实现 1、HashSet 的底层实现2、 HashSet 的特点3、 总结 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; HashSet 是 Java 集合框架中的一个重要成员&#xff0c;它提供了不存储重复元素的集合。但是&am…

【AI作曲】毁掉音乐?早该来了!一个网易音乐人对于 AI 大模型音乐创作的思辨

引言&#xff1a;AI在创造还是毁掉音乐&#xff1f; 正如当初 midjourney 和 StableDiffusion 在绘画圈掀起的风波一样&#xff0c;suno 和 各大音乐大模型的来临&#xff0c;其实早该来了。 AI 在毁掉绘画&#xff1f;或者毁掉音乐&#xff1f; 没错&#xff0c;但也错了。…