React setState 的实现机制

10 min read

实现机制

React的生命周期和合成事件中,React仍然处于它的批处理更新机制中,这时无论调用多少次 setState,都不会立即执行更新,而是将要更新的state存入_pendingStateQueue,将要更新的组件存入dirtyComponent。当上一次更新机制执行完毕,最顶层组件didmount后会将批处理标志设置为 false。这时将取出dirtyComponent中的组件以及 _pendingStateQueue中的state进行更新。这样就可以确保组件不会被重新渲染多次。

当我们在执行 setState后立即去获取 state,这时是获取不到更新后的 state的,因为处于React的批处理机制中,state被暂存起来,待批处理机制完成之后,统一进行更新。

如何获取React setState后的值

setState的第二个参数接收一个回调函数,该函数会在 React的批处理机制完成之后调用,所以你想在调用 setState后立即获取更新后的值,请在该回调函数中获取。

this.setState(
    {
        index: this.state.index + 1
    },
    () => {
        console.log(this.state.index);
    }
)

React会对多次连续的 setState进行合并,如果你想立即使用上次 setState后的结果进行下一次 setState,可以让 setState 接收一个函数而不是一个对象。这个函数用上一个 state 作为第一个参数,将此次更新被应用时的 props 做为第二个参数。

componentDidMount() {
    this.setState(
        (state, props) => ({
            index: state.index + 1
        }),
        () => {
            console.log(this.state.index);
        }
    )
    this.setState(
        (state, props) => ({
            index: state.index + 1
        }),
        () => {
            console.log(this.state.index);
        }
    )
}

setState到底是同步还是异步的

 componentDidMount() {
    setTimeout(() => {
        console.log('调用setState');
        this.setState({
            index: this.state.index + 1
        })
        console.log('state', this.state.index);
    }, 0);
}

// 原生事件
dom.addEventListener('click',()=>{
    setState({foo: 'bar'})
})

如上面的代码,当我们在异步代码中调用 setState时,根据 JS的异步机制,会将异步代码先暂存,等所有同步代码执行完毕后在执行,这时 React的批处理机制已经走完,批处理标志设被设置为 false,这时再调用 setState即可立即执行更新,拿到更新后的结果。

原生事件中调用 setState并不会触发 React的批处理机制,所以立即能拿到最新结果。

所以异步只是假象,合并更新,标记更新才是其本质