美文网首页
React.setState之state批处理的机制

React.setState之state批处理的机制

作者: 小进进不将就 | 来源:发表于2019-03-05 13:58 被阅读0次

前言:之前讨论过 setState 的机制,传送门:https://www.jianshu.com/p/9a6654269922


setState 的状态更新是异步的,但调用多个 setState的时候,它们是按顺序来更新的吗?
例:
在同一组件下:

export default class Child extends Component {
  state={
      a:false,
      b:false,
   }

  componentDidMount() {
    this.setState({a:true})
    this.setState({b:true})
  }
  
  render() {
    return <div>
    </div>
  }
}

b 会不会在 a 之前变成 true?


在不同组件下:
父:

export default class App extends Component {
  state={
      a:false,
  }
  
  changeA(){
    this.setState({a:true})
  }

  render() {
    return (
        <Child changeA={this.changeA.bind(this)}/>
    );
  }
}

子:

export default class Child extends Component {
    state={
      b:false,
    }

  componentDidMount() {
    this.props.changeA() //改变父组件的a属性
    this.setState({b:true})
  }

  render() {
    return <div>
    </div>
  }
}

子组件的 b 会不会在 父组件的 a 之前变成 true ?


答案是:b 都不会在 a 之前变成 true,setState 的批量更新按照先进先出的原则,顺序更新。

在 React v16 及之前的版本中,可以通过 unstable_batchedUpdates() 跳出 React 的更新队列,在下一批次来强制更新,不走 React 默认的顺序批次处理 的流程,但是在 React v17或更高版本,会取消掉 unstable_batchedUpdates() 这个不稳定的API,让 React 中的所有 setState 按照顺序依次更新。

(1)在同一个 event handler(事务)中
理解的关键在于:无论你在多少个组件中调用多少个 setState,它们都会在最后一次 setState 后,全部放在同一个队列里,然后执行一个统一的更新,而不会说是 在父组件 re-render 一次,在子组件又 re-render 一次。

例:

export default class Child extends Component {
  constructor(props){
    super(props)
    this.state={
      b:false,
      c:false,
    }
  }

  componentDidMount() {
    this.setState({b:true})
    console.log(this.state,'first')
    this.setState({c:true})
    console.log(this.state,'second')
  }

  render() {
    console.log(this.state,'render')
    return <div>
    </div>
  }
}

结果:

{b: false, c: false} "render"
{b: false, c: false} "first"
{b: false, c: false} "second"
{b: true, c: true} "render"

(2)但是在 Ajax、setTimeout 等异步方法中,每 setState 一次,就会 re-render 一次
例:

export default class Child extends Component {
  constructor(props){
    super(props)
    this.state={
      b:false,
      c:false,
    }
  }

  componentDidMount() {
    let aPromise = new Promise((resolve)=> {
      resolve(100)
    })
    aPromise.then((value)=> {
      this.setState({b:true}) //re-render
      console.log(this.state,'first')
      this.setState({c:true}) //re-render
      console.log(this.state,'second')
    });
  }

  render() {
    console.log(this.state,'render')
    return <div>
    </div>
  }
}

结果:

{b: false, c: false} "render"
{b: true, c: false} "render"
{b: true, c: false} "first"
{b: true, c: true} "render"
{b: true, c: true} "second"

(2-1)在异步调用的方法中(promise.then()、setTimeout)通过 unstable_batchedUpdates() 强制将 多个setState 置在同一更新队列中,一起更新

import React, { Component,Fragment } from 'react';
import * as ReactDOM from "react-dom";

export default class Child extends Component {
  constructor(props){
    super(props)
    this.state={
      b:false,
      c:false,
      d:false,
    }
  }

  componentDidMount() {
    let aPromise = new Promise((resolve)=> {
      resolve(100)
    })
    aPromise.then((value)=> {
      // Forces batching
      ReactDOM.unstable_batchedUpdates(() => {
        this.setState({b:true}) // Doesn't re-render yet
        console.log(this.state,'first')
        this.setState({c:true}) // Doesn't re-render yet
        console.log(this.state,'second')
      })
      // When we exit unstable_batchedUpdates, re-renders once
    });
  }

  render() {
    console.log(this.state,'render')
    return <div>
    </div>
  }
}

结果:

{b: false, c: false} "render"
{b: false, c: false} "first"
{b: false, c: false} "second"
{b: true, c: true} "render"

(2-2)在异步调用的方法里(setTimeout、promise.then())使用 unstable_batchedUpdates() 更新的队列排在 React 默认队列的后面

import React, { Component,Fragment } from 'react';
import * as ReactDOM from "react-dom";

export default class Child extends Component {
  constructor(props){
    super(props)
    this.state={
      b:false,
      c:false,
      d:false,
    }
  }

  componentDidMount() {
    let aPromise = new Promise((resolve)=> {
      resolve(100)
    })
    aPromise.then((value)=> {
      ReactDOM.unstable_batchedUpdates(() => {
        this.setState({b:true})
        this.setState({c:true})
      })
    });
    this.setState({d:true,})
  }

  render() {
    console.log(this.state,'render')
    return <div>
    </div>
  }
}

结果:

{b: false, c: false, d: false} "render"
{b: false, c: false, d: true} "render"
{b: true, c: true, d: true} "render"

注意:React.setState 的内部更新队列,本质上就是放在 unstable_batchedUpdates() 里的,但如果在 unstable_batchedUpdates() 中嵌套 unstable_batchedUpdates() 的话,React 是不会 re-render 两次的,React 会找到 最外层的 unstable_batchedUpdates ,然后将里面所有的 setState 放到同一个队列中,render 一次

注意:setState中的对象合并都是->浅合并

参考:https://stackoverflow.com/questions/48563650/does-react-keep-the-order-for-state-updates?answertab=active#tab-top


(完)

相关文章

网友评论

      本文标题:React.setState之state批处理的机制

      本文链接:https://www.haomeiwen.com/subject/gpzbuqtx.html