react state深入解析

104 阅读2分钟

案例1

观察如下代码,点击+按钮后,控制台和页面效果如下

import React,{useRef, useState} from "react"
const Page1 = () => {
  const [num,setNum] = useState(0)
  const divRef = useRef(null)
  const addNum = () => {
    setNum(num+1)
    console.log(divRef.current.innerText)
  }
  return (
    <div>
      <div ref={divRef}>num:{num}</div>
      <button onClick={addNum}>+</button>
    </div>
  )
}
export default Page1

结论1 react的state在发生更新后,并不是同步渲染dom到页面,也就是dom的渲染也是异步执行。

案例2

const [num,setNum] = useState(0)

const addNum = () => {
    console.log('begin',num)
    setNum(num+1)
    console.log('end',num)
}

<div onClick={addNum}></div>

结论2 react的setState是异步执行,因此执行setNum后,无法获取最新的num.

案例3

观察代码输出,发现num更新后,可以拿到num的最新数据,

import React,{useRef, useState} from "react"
const Page1 = () => {
  const [num,setNum] = useState(0)
  const addNum = () => {
    setNum(e=>{
      let newVal = e+1 
      console.log(newVal)
      return newVal
    })
  }
  return (
    <div>
      <div>num:{num}</div>
      <button onClick={addNum}>+</button>
    </div>
  )
}
export default Page1

分析

查看setStata函数参数的ts定义,可以发现setState的参数可以传递两种类型

  1. 传递的数据类型

  2. 函数,函数的参数是state,并且setState传递函数可以实现同步更新state

     type React.SetStateAction<S> = S | ((prevState: S) => S)
     
    

验证

    const [num,setNum] = useState(0)
    const addNum = () => {
        console.log('1')
        setNum(e=>{
            let newV = e+1
            console.log('2')
            return newV
        })
        console.log('3')
    }
    
    <div onClick={addNum}></div>
    
    

页面打印输出1,2,3,符合要求。

结论3 react的setState传递变量是异步执行操作,而传递函数可以实现同步更新state。还可以获取获取最新的state。

案例4

import React, { useState } from 'react';

const BatchUpdateExample = () => {
  console.log('app load')
  const [count1, setCount1] = useState(0);
  const [count2, setCount2] = useState(0);
	
  const handleClick = () => {
    setCount1(count1 + 1);
    setCount2(count2 + 1);
  };

  return (
    <div>
      <p>Count1: {count1}</p>
      <p>Count2: {count2}</p>
      <button onClick={handleClick}>Increment</button>
    </div>
  );
};

观察上述代码,页面render几次?答案是1次。react批量更新机制让count1和count2的更改只渲染一次。

无批量更新

无批量更新机制时,n个state发生更改,页面render了n次

graph TD
收集依赖state1 --> 更新1 --> 渲染state1到页面
收集依赖state2 --> 更新2 --> 渲染state2到页面
... --> .... --> .....

批量更新

graph TD
收集依赖state1  --> 添加更新队列--> 批量更新--> 批量渲染dom
收集依赖state2  --> 添加更新队列--> 批量更新--> 批量渲染dom
收集依赖staten  --> 添加更新队列--> 批量更新--> 批量渲染dom

案例5

一次按钮点击,同时执行三次setState,页面最后显示?

import React,{useRef, useState} from "react"
const Page2 = () => {
  const [num,setNum] = useState(0)
  const addNum = () => {
    setNum(num+1)
    setNum(num+2)
    setNum(num+3)
  }
  return (
    <div>
      <div>num:{num}</div>
      <button onClick={addNum}>+</button>
    </div>
  )
}
export default Page2

结果是3,setState对同一份数据批量修改,只有最后一次修改会被执行。

结论:对同一个state执行多次更新操作,react永远只执行最后一次的setState更新。

如何让多个setState同时生效?只需要setState传递函数即可。如下num最新值是6

setNum(num=>num+1)
setNum(num=>num+2)
setNum(num=>num+3)