React State

79 阅读2分钟

在不同的执行环境下,或者不同的 React 模式下,State 更新流程都是不同的。

state 到底是同步还是异步的?

React 是有多种模式的,基本平时用的都是 legacy 模式下的 React,除了legacy 模式,还有 blocking 模式和 concurrent 模式, blocking 可以视为 concurrent 的优雅降级版本和过渡版本,React 最终目的,不久的未来将以 concurrent 模式作为默认版本,这个模式下会开启一些新功能。对于 concurrent 模式下,会采用不同 State 更新逻辑

类组件中的setState

假如一次事件中触发一次如上 setState ,在 React 底层主要做了那些事呢?

  • 首先,setState 会产生当前更新的优先级(老版本用 expirationTime ,新版本用 lane )。
  • 接下来 React 会从 fiber Root 根部 fiber 向下调和子节点,调和阶段将对比发生更新的地方,更新对比 expirationTime ,找到发生更新的组件,合并 state,然后触发 render 函数,得到新的 UI 视图层,完成 render 阶段。
  • 接下来到 commit 阶段,commit 阶段,替换真实 DOM ,完成此次更新流程。
  • 此时仍然在 commit 阶段,会执行 setState 中 callback 函数,如上的()=>{ console.log(this.state.number) },到此为止完成了一次 setState 全过程 更新的流程图如下:

02.jpg

事件

在 React 事件执行之前通过 isBatchingEventUpdates=true 打开开关,开启事件批量更新,当该事件结束,再通过 isBatchingEventUpdates = false; 关闭开关,然后在 scheduleUpdateOnFiber 中根据这个开关来确定是否进行批量更新。 用定时器promise等打破批量更新 使用unstable_batchedUpdates手动批量更新 使用flushSync提升更新优先级

两个面试理解题

export default class index extends React.Component{
    state = { number:0 }
    handleClick= () => {
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback1', this.state.number)  })
          console.log(this.state.number)
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback2', this.state.number)  })
          console.log(this.state.number)
          this.setState({ number:this.state.number + 1 },()=>{   console.log( 'callback3', this.state.number)  })
          console.log(this.state.number)
    }
    render(){
        return <div>
            { this.state.number }
            <button onClick={ this.handleClick }  >number++</button>
        </div>
    }
} 

点击打印:0, 0, 0, callback1 1 ,callback2 1 ,callback3 1

const handleClick = ()=>{
    ReactDOM.flushSync(()=>{
        setNumber(2) 
        console.log(number) 
    })
    setNumber(1) 
    console.log(number)
    setTimeout(()=>{
        setNumber(3) 
        console.log(number)
    })   
}

点击打印:000

函数组件有个浅比较不更新的问题,比如数组和对象,赋值后对比不更新,这时候直接重新赋值{...},[...] 直接重新申请内存地址