setState基本使用与异步更新

51 阅读3分钟

在vue中

  1. 代码编译过程: template -> 通过compile部分 进行转换成 -> render()也就是[h("div",{props},children)]

  2. 在修改响应式变量时候。他会触发该变量的set方法 然后进行重新执行render函数

在React中

  1. 代码通过babel编译 拿到render() -> 通过React.createElement("div",{},children)
  2. 在修改响应式变量时候,必须手动调用this.setState()修改的值和原值一样的话。他内部也会重新调一次render() 这就造成了不必要的渲染

这个时候可以使用钩子函数 shouldComponetUdate(preState) 在内部 进行判断 { if(preState.message===this.state.message){return false} } 也可以使用PureComponent

因为React 中他没有vue中的方式 对数据进行了数据劫持 所以必须通过setState来告知React数据已经发生了变化 其中setState方法是从Component方法中继承过来的

image.png

setState 使用

  1. 直接使用 在this.SetState({ message:1 }) 过程中 他在源码中其实就是使用了Object.Assign(this.state,newState) 在合适的时机下 调用render()

  2. 函数方式使用 this.steState((state,props)=>{return {message:1,age:state.opo})

  • 好处一 可以在回调函数中编写新的state逻辑
  • 好处二 可以使用之前的state和props参数
  1. setState 函数处理中是一个异步调用 我们并不能在执行完setState之后立马拿到最新的state的结果 如果希望数据更新后立刻获取到最新的值。setState 会有第二个参数callback this.setState({message:'修改'},()=>{return this.state.message})

setState 异步调用

在React 18 之前 有些操作是setState是个同步操作的 但是在React 18 之后 就会全部变成异步操作

可以使用 给setState 包裹在一个宏任务队列里 这样setState就会在浏览器执行宏任务队列时候来回调执行该函数 这就跟React事件执行没关系了 这样就会把setState变成一个同步操作 setTimeout(()=>{this.setState({message})},0)

原生DOM中监听 addEventListener

promise回调

但是在React18之后 把所有宏任务的回调函数 都会变成批量处理 异步操作

1.为什么setState 设置为异步操作

React核心成员(Redux的作者)Dan Abramov也有对应的回复 github.com/facebook/re…;

  • 可以显著提升性能
  1. 如果每次调用setState都进行一次更新的话,那么意味着render函数会频繁调用 界面多次渲染 虚拟DOM将会做多次diff算法 浏览器多次回流与重制 这样效率会很低的
  2. 最好的办法就是获取到多个更新,之后进行批量更新
  • 如果同步更新了state 但是还没有执行render函数 那么state和props将不能同步
  1. state和props不能保持一致性,将会导致页面渲染的值和state目前的值 不一致
  • setState的批量处理
  1. 就是把setState放进一个队列(先进先出里面 然后会讲依次合并 其实内部是做了一个do while循环 等合并结束了 再统一执行render函数

如果要使用最新的state的话 可以用 this.steState((state,props)=>{return {message:1,age:state.opo}) 这个参数state是最新的state值 但是在函数内部使用this.state.message这个值还是更新之前

  • 如果希望setState进行同步处理的话 使用 从react-dom包中获取到flushSync

flushSync在函数内部还是批处理 flushSync(()=>{this.setState({message:1})})