【React】setState进阶

前言

在React18中,setState操作都是异步的「不论是在哪执行,比如:合成事件,周期函数,定时器」,

目的:实现状态的批处理「统一处理」

  • 有效减少更新次数,降低性能消耗
  • 有效管理代码执行的逻辑顺序

原理:利用了更新队列「updater」机制来处理

  • 在当前相同时间段内「浏览器此时可以处理的事情中」,遇到setState会立即放入到更新队列
  • 此时状态/视图微更新
  • 当所有的代码操作结束,会“刷新队列”「通知更新队列中的任务执行」:把所有放入到setState合并一起执行,只触发一次视图更新「批处理操作」

React18 和 React16 关于 setState的区别

在React18 和 React16中,关于setState是同步还是异步,是有一些区别的!

  • React18中:不论在什么地方执行setState,它都是异步的「都基于updater更新队列机制,实现的批处理」
  • React16中:如果在合成事件「jsx元素中基于onXXX绑定的事件中」,周期函数中,setState的操作是异步的!!但是如果setState出现在其他异步操作中「例如:定时器、手动获取DOM元素做的事件绑定(元素.addEventListener())等」,它将变为同步操作「立即更新状态和让视图渲染」

在这里插入图片描述

打印效果如下:
在这里插入图片描述

setState语法

this.setState([partialState],[callback])
  • [partialState]:支持部分状态更新
	 this.setState({
    
    
		 x:100 //不论总共多少状态,只修改了x,其余状态不动
	  })
  • [callback]:在状态更改,视图更新完毕后触发「只要执行setState,callback一定执行」

    • 发生在componentDidUpdate周期函数之后「componentDidUpdate会在任何状态更改之后触发执行」,而回调函数方式,可以在指定状态更新之后处理一些逻辑
    • 特殊:即便基于shouldComponentUpdate阻止了状态/视图更新,componentDidUpdate周期函数肯定不会执行,但是设置的callback回调函数依然会触发执行

setState对render的影响

1、同时修改三个状态值

只渲染一次,render中只打印一次。


class Demo extends React.Component {
    
    
    state = {
    
    
        x: 10,
        y: 5,
        z: 0
    };

    handle = () => {
    
    
        let {
    
     x, y, z } = this.state;
        this.setState({
    
    
            x:x+1,
            y:y+1,
            z:z+1
        });
    };

    render() {
    
    
        console.log('视图渲染:RENDER');
        let {
    
     x, y, z } = this.state;
        return <div>
            x:{
    
    x} - y:{
    
    y} - z:{
    
    z}
            <br />
            <button onClick={
    
    this.handle}>按钮</button>
        </div>;
    }
}

export default Demo;

2、多个setState同时修改状态值

不会立即修改状态和视图,而是放到updater更新队列。当上下文代码执行完之后,则执行更新队列,只更新一次,且统一渲染

 handle = () => {
    
    
        let {
    
     x, y, z } = this.state;
        this.setState({
    
     x: x + 1 });
        console.log(this.state.x);//10
        this.setState({
    
     y: y + 1 });
        console.log(this.state.y);//5
        this.setState({
    
     y: y + 1 });
        console.log(this.state.z);//0
 };

在这里插入图片描述

3、遇到异步事件的时

先合并渲染一次,等异步执行时再渲染一次

 handle = () => {
    
    
        let {
    
     x, y, z } = this.state;
        this.setState({
    
     x: x + 1 });
        this.setState({
    
     y: y + 1 });
        console.log(this.state.x,this.state.y);

        setTimeout(() => {
    
    
            this.setState({
    
     z: z + 1 });
            console.log(this.state.z);
        }, 1000);
    };

在这里插入图片描述

4、多个异步事件

点击按钮之后在,由于三个定时器时间差为0(如果时间差很小很小也可以),页面只会渲染一次。

  • 在当前相同时间段内「浏览器此时可以处理的事情中」,遇到 setState会立即放入到更新队列
  • 此时状态/视图微更新
  • 当所有的代码操作结束,会“刷新队列”「通知更新队列中的任务执行」:把所有放入到setState合并一起执行,只触发一次视图更新「批处理操作」
 handle = () => {
    
    
     setTimeout(() => {
    
    
            this.setState({
    
     x: x + 1 });
            console.log(this.state.z);
        }, 1000);
      setTimeout(() => {
    
    
            this.setState({
    
     y: y + 1 });
            console.log(this.state.z);
        }, 1000);

       setTimeout(() => {
    
    
            this.setState({
    
     z: z + 1 });
            console.log(this.state.z);
        }, 1000);
    };

5、flushSync

flushSync 是 React 18 引入的一个 API,需要从react-dom中导入,旨在强制同步更新状态。它使得开发者能够在某些情况下阻止 React 的批量更新机制,并立即刷新组件。

在 React 中,状态更新通常是异步的,即 React 会将多个状态更新合并到一个渲染周期中,减少不必要的 DOM 更新。这种行为称为“批量更新”。但是在某些情况下,开发者希望在执行某个操作时,立即触发渲染(而不等待 React 的批量更新)。

import {
    
     flushSync } from "react-dom";

  handle = () => {
    
    
        let {
    
     x, y, z } = this.state;
        this.setState({
    
     x: x + 1 });
        flushSync(()=>{
    
    
            this.setState({
    
     y: y + 1 });
            console.log(this.state);
        })
        this.setState({
    
     z: this.state.x+this.state.y });
        console.log(this.state);
    };

点击按钮之后,视图渲染两次,数据的状态也发生了变化。其实是在flushSync操作结束之后,会立即刷新更新队列。
在这里插入图片描述
具体逻辑如下图:
在这里插入图片描述

6、拓展

1、循环里的setState
点击按钮之后,循环20次,视图只会渲染一次,最终x在视图上显示为x->11

  handle = () => {
    
    
        for(let i =0;i<20;i++){
    
    
            this.setState({
    
     x: this.state.x + 1 });
        }
    };

2、循环里使用setState、flushSync
点击按钮之后,循环20次,更新了20次,最终显示为30

  handle = () => {
    
    
    for(let i =0;i<20;i++){
    
     
         flushSync(()=>{
    
    
                this.setState({
    
     x: this.state.x + 1 });
            })  
       }  
  };

或者

  handle = () => {
    
    
    for(let i =0;i<20;i++){
    
     
     this.setState({
    
     x: this.state.x + 1 });
     flushSync();
     }
  };

3、循环20次,只渲染1次

  handle = () => {
    
    
    for(let i =0;i<20;i++){
    
     
         this.setState(prevState=>{
    
    
                return {
    
    
                    x:prevState.x +1
                }
            })
     }
  };

prevState => { return { x: prevState.x + 1 } }

这是一个函数作为 setState 的参数。在这种方式下,prevState 是一个回调函数的参数,它代表当前组件的最新状态。
prevState.x + 1 是根据当前的状态 prevState.x 计算出新的值。也就是说,每次调用 setState 时,x 的值会基于之前的值增加 1

猜你喜欢

转载自blog.csdn.net/qq_38951259/article/details/145450999