学习笔记:React中setState的执行机制

32 阅读2分钟

参考:setState是同步还是异步?原理是什么?

setState是什么?

setState是用于更新组件状态的方法,React保证在setState执行之后会调用render函数进行页面渲染更新

  1. 第一个参数可以是一个对象或者是一个函数
  2. 第二个参数是一个回调函数,获取更新之后的数据
import React, { component } from 'react'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            message: 'hello',
        }
    }
    changeMessage() {
        this.setState({
            message: 'hello world'
        })
    }
    
    render() {
        return (
            <div>
                <h2>{this.state.message}</h2>
                <button onClick={e => this.changeMessage()}>update message</button>
            </div>
        )
    }
}

更新类型

  1. 异步更新

setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数setState(partialState, callback)中的callback拿到更新后的结果

  • 在组件生命周期钩子(除componentDidUpdate) 或 React合成事件中,为异步更新
import React, { component } from 'react'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            message: 'hello',
        }
    }
    changeMessage() {
        this.setState({
            message: 'hello world'
        }, () => {
            console.log(this.state.message) // hello world
        })
        
        console.log(this.state.message) // hello
    }
    
    render() {
        return (
            <div>
                <h2>{this.state.message}</h2>
                <button onClick={e => this.changeMessage()}>update message</button>
            </div>
        )
    }
}
  1. 同步更新
  • 使用setTimeout包裹时,变为同步更新
import React, { component } from 'react'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            message: 'hello',
        }
    }
    changeMessage() {
        // 使用setTimeout包裹时,变为同步更新
        setTimeout(() => {
            this.setState({
                message: 'hello world'
            })
            console.log(this.state.message) // hello world
        }, 0)
        
    }
    
    render() {
        return (
            <div>
                <h2>{this.state.message}</h2>
            </div>
        )
    }
}
  • 使用原生事件进行事件绑定时,变为同步更新
import React, { component } from 'react'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            message: 'hello',
        }
    }
    componentDidMount() {
        const btn = document.getElementById('btn')
        
        // 使用原生事件进行事件绑定时,变为同步更新
        btn.addEventListen('click', () => {
            this.setState({
                message: 'hello world'
            })
            
            console.log(this.state.message) // hello world
        })
    }
    
    render() {
        return (
            <div>
                <h2>{this.state.message}</h2>
                <button id='btn'>update message</button>
            </div>
        )
    }
}

小结

在组件生命周期 或 React合成事件中,为异步更新
在setTimeout中调用 或 使用原生事件进行事件绑定时调用,变为同步更新页面上显示为2 } render() { return ( <div> <h2>{this.state.message}</h2> <button onClick={e => this.changeMessage()}>update message</button> </div> ) } }

3. 批量更新

import React, { component } from 'react'

export default class App extends Component {
    constructor(props) {
        super(props)
        this.state = {
            count: 1,
        }
    }
    addCount() {
        this.setState({
            count: this.state.count + 1,
        })
        console.log(this.state.count) // 1
        
        this.setState({
            count: this.state.count + 1,
        })
        console.log(this.state.count) // 1
        
        this.setState({
            count: this.state.count + 1,
        })
        console.log(this.state.count) // 1
        
        this.setState({
            count: this.state.count + 1,
        })
        console.log(this.state.count) // 1
        
        // 批量更新,覆盖操作,取最后一次执行的结果,此时页面上显示为2 
        // 等价于如下操作,只取最后一次
        Object.assign(previousState, {
            {count: this.state.count + 1},
            {count: this.state.count + 1},
            {count: this.state.count + 1},
            {count: this.state.count + 1},
        })
    }
    
    render() {
        return (
            <div>
                <h2>{this.state.count}</h2>
                <button onClick={e => this.addCount()}>update message</button>
            </div>
        )
    }
}
  • 若第一个参数使用函数,虽然还是批量更新,但是由于可以拿到preState,因此可以顺利完成累加;不过第二个参数的回调函数会在批量更新结束后统一按顺序调用,因此输出结果都为最终结果
addCount() {
        this.setState((preState, props) => {
            return {count: preState.count + 1}
        }, () => {
            console.log('callback: ', this.state.count) // callback: 5
        })
        console.log('@: ', this.state.count) // @: 1
        
        this.setState((preState, props) => {
            return {count: preState.count + 1}
        }, () => {
            console.log('callback: ', this.state.count) // callback: 5
        })
        console.log('@: ', this.state.count) // @: 1
        
        this.setState((preState, props) => {
            return {count: preState.count + 1}
        }, () => {
            console.log('callback: ', this.state.count) // callback: 5
        })
        console.log('@: ', this.state.count) // @: 1
        
        this.setState((preState, props) => {
            return {count: preState.count + 1}
        }, () => {
            console.log('callback: ', this.state.count) // callback: 5
        })
        console.log('@: ', this.state.count) // @: 1
        
    }