
前言:之前讨论过 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中的对象合并都是->浅合并
(完)
网友评论