setState最常见的问题是,是异步的还是同步的?

setState在React.Component中的函数,是同步函数。但是我们调用的时候,不同的传参和不同的调用位置都会导致不同的结果。

从页面看有时候是同步的,有时候是异步的。

PS: 如果在componentDidMount触发,和用按钮事件触发是一样的。

1)调用函数中直接调用,调用完之后立即打印,结果不变,表现是异步

this.state = {number: 0}
add = () => {
   this.setState({
       number: this.state.number + 1}, () => console.log(this.state.number) // 1
   )
  // console.log(this.state.number); -- 0
}  // 页面显示1

后台逻辑是,事件触发-->开启批量更新模式-->执行事件方法add-->状态存入队列[{number: 1}]-->方法结束(this.state.number=0)-->关闭批量更新模式

-->更新状态(number-1)-->更新组件(render)-->componentDidUpdate-->setState.callback(number-1)

2)批量调用setState会触发批量更新

如第一个参数是对象,状态会出现覆盖;第一个参数是函数,不会被覆盖,会累计。

    state = {number: 0, name: 'lyra'}
    add2 = () => {
        this.setState((state) => ({number: state.number + 1}));
        this.setState((state) => ({name: state.name + '-good'}));
        this.setState((state) => ({number: state.number + 1}));
        this.setState((state) => ({name: state.name + '-clever'}));
    }// 执行完结果显示{number:2, name: lyra-good-clever}
    add3 = () => {
        this.setState({number: this.state.number + 1});
        this.setState({name: this.state.name + '-good'});
        this.setState({number: this.state.number + 1});
        this.setState({name: this.state.name + '-clever'}); 
    } // 执行完结果显示{number: 1, name: lyra-clever}

后台逻辑同上,会依次存入状态队列。但是更新状态的方式是:

this.queue.pendingStates.forEach(particalState => Object.assign(this.state, particalState));

如果传入对象,存储状态的时候直接存储。

        if (typeof particalState === 'object') {
            this.pendingStates.push(particalState);
        }

如果传入的是方法,会先进行处理,每个状态,都是以前一个状态为基础

        if (typeof particalState === 'function') {// 传入的是(state) => {}
            this.setStateFuncs.push(particalState);
            this.setStateFuncs.forEach(callback => {
                const newParticalState = callback(this.component.state);
                this.pendingStates.push(newParticalState);
                Object.assign(this.component.state, newParticalState);
            });
            this.setStateFuncs.length = 0;// 注意清空!!
        }

3)在异步任务回调函数中表现同步,且不批量更新

class Test extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            number: 0,
            name: 'lyra',
        }
    }
    componentDidMount() {
        this.add();
    }
    add = () => {
        this.setState({number: this.state.number + 1}, () => console.log('callback-3=',this.state));
        this.setState({name: this.state.name + '-good'}, () => console.log('callback-4=',this.state)); 
        this.setState({number: this.state.number + 1}, () => console.log('callback-5=',this.state));
        this.setState({name: this.state.name + '-clever'}, () => console.log('callback-6=',this.state));
        console.log('==1==',this.state); 
        setTimeout(() => {
            console.log("==7===",this.state); 
            this.setState({ // 非批量处理,立即执行,触发render
                number: this.state.number + 1,
                name: this.state.name + '-bravo'
            }, () => console.log('callback-8=',this.state))
            console.log('==9==',this.state);
            this.setState({
                number: this.state.number + 1,
                name: this.state.name + '-congratulations'
            }, () => console.log('callback-10=',this.state))
            console.log('==11==',this.state); 
        }) 
        console.log("==2==",this.state);
    }
    render() {
        console.log('render')
        return (
            <h1>Hello, world!</h1>
        )
    }
}

运行结果如下

    ==1=={number: 0, name: 'lyra'}
    ==2=={number: 0, name: 'lyra'}
    render
    callback-3={number:1, name: 'lyra-clever}
    callback-4={number:1, name: 'lyra-clever}
    callback-5={number:1, name: 'lyra-clever}
    callback-6={number:1, name: 'lyra-clever}
    ==7==={number: 1, name: 'lyra-clever'}
    render
    callback-8={number: 2, name: 'lyra-clever-bravo'}
    ==9=={number: 2, name: 'lyra-clever-bravo'}
    render
    callback-10={number: 3, name: 'lyra-clever-bravo-congratulations'}
    ==11=={number: 3, name: 'lyra-clever-bravo-congratulations'}

后台逻辑: 事件触发-->开启批量更新模式-->执行事件方法主线程(不包含setTimeout宏任务)-->前四个状态存入队列{number: 1}-->主线程结束-->

关闭批量更新模式-->批量更新状态(this.state.number = 1)并渲染(DidUpdate)-->进入SetTImout-->setState-->非批量更新状态立即更新状态并渲染(DidUpdate)-->callback->

-->下一个setState-->......

 

PS:原生JS模拟setState实现

html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
    <div id="root"></div>
    <script src="./component.js"></script>
    <script src='./counter.js'></script>
    <script src="./stateFunc.js"></script>
    <script>
        let container = document.getElementById('root');
        counterIns = new Counter({name: 'Lyra'}); //实例传属性
        counterIns1 = new SetFuncCounter({name: 'Lyra1'}); //实例传属性
        counterIns.mount(root); // ReactDOM.render(,window.root)
        counterIns1.mount(root);
    </script>
</body>
</html>
View Code

相关文章: