React setState - "Asynchronous"

Posted by jscruggs on Mon, 14 Oct 2019 04:14:50 +0200

The execution of setState is an'asynchronous'process. Why do we use quotation marks to replace these two words? It is certain that there is some truth in them. Here is an analysis of setState.

setState source code analysis (v16.10.0)

React source code In setState, two parameters partialState, callback are passed in:

setState

  • Partial State literally means partial state. The annotation says that Next partial state or function to produce next partial state to be merged with current state. Probably translation is the next partial state or function to produce the next partial state to be merged with the current state. In fact, when this.setState({}) updates the specified state in the project, the other states are unaffected, and all States are merged with the current updated state.
  • Callback callback function, which means a callback after status updates, gets the latest state in this function.
  • setState process

invariant() is called first, and this.updater.enqueueSetState() is called second.

/ Packetages/shared/invariant.js, this method is to determine whether the type of partialState is correct, throw an error, and attach the source code:

/ Packetages/react-dom/src/server/ReactPartialRenderer.js, to update the state push to queue, in the new Component, updater will be passed in, with source code:

Queue should be the key to React's performance improvement. Because not every time setState is called, React updates immediately, but every time setState is called, React just push es it into the queue to be updated, with the source code attached:

/ enqueueSetState in packages/react-reconciler/src/ReactFiberClassComponent.js;

/ enqueueUpdate in packages/react-reconciler/src/ReactUpdateQueue.js;

process analysis

  • 1. Hook function and synthesis method -- setState
import React from 'react';
import {Button} from 'antd';

class SetState extends React.Component {

    state = { val: 0 }

    componentDidMount() {
        this.setState({ val: this.state.val + 1 })
       console.log(this.state.val) // Output or pre-update value - > 0
    }
    
    increment = () => {
        this.setState({ val: this.state.val + 1 })
        console.log(this.state.val) // The output is val - > 0 before the update
    }

    render() {
        return (
            <Button type="primary" onClick={this.increment}>
                //Hook function and synthesis method - > {Counter is: ${this. state. val} `}
            </Button>
        )
    }
}

export default SetState;
  • 2. Native js event -- setState
import React from 'react';
import {Button} from 'antd';

class SetState extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            val: 0
        }
    }

    componentDidMount() {
       document.body.addEventListener('click', this.changeValue, false)
    }

    changeValue = () => {
        this.setState({ val: this.state.val + 1 })
        console.log(this.state.val) // Output is the updated value - > 1
    }

    render() {
        return (
            <Button type="peimary">
                //Native js event - > {Counter is: ${this. state. val} `}
            </Button>
        )
    }
}

export default SetState;
  • 3. setTimeout timer -- setState
import React from 'react';
import {Button} from 'antd';

class SetState extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            val: 0
        }
    }

    componentDidMount() {
        setTimeout(_ => {
            console.log('implement->The second step', this.state.val) // Output value before update - > 0
            this.setState({ val: this.state.val + 1 })
            console.log('implement->The third step', this.state.val) // Output updated value - > 1
        }, 0);
        console.log('implement->First step', this.state.val) // Output value before update - > 0
    }

    render() {
        return (
            <Button type="primary">
                setTimeout timer --> {`Counter is: ${this.state.val}`}
            </Button>
        )
    }
}

export default SetState;
  • 4. Batch Update - setState
import React from 'react';
import {Button} from 'antd';

class SetState extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            val: 0
        }
    }

    batchUpdates = () => {
        this.setState({ val: this.state.val + 1 })
        this.setState({ val: this.state.val + 1 })
        this.setState({ val: this.state.val + 1 })
    }

    render() {
        return (
            <Button type="primary" onClick={this.batchUpdates}>
                //Batch Update - > {Counter is: ${this. state. val} `}
            </Button>
        )
    }
}

export default SetState;
  • 5. Hook function and setTimmout timer--the result of setState execution
import React from 'react';
import {Button} from 'antd';

class SetState extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            val: 0
        }
    }

    // The setState in the hook function cannot get the updated value immediately.
    // The batch update strategy of setState;
    // setState in setTimmout is able to get the update results synchronously
    componentDidMount() {
        this.setState({ val: this.state.val + 1 })
        console.log('Step one:', this.state.val); // 0
    
        this.setState({ val: this.state.val + 1 })
        console.log('The second step:', this.state.val); // 0
    
        setTimeout(_ => {
            this.setState({ val: this.state.val + 1 })
            console.log('The third step:', this.state.val); // 2
        
            this.setState({ val: this.state.val + 1 })
            console.log('The fourth step:', this.state.val); //  3
        }, 0)
    }

    render() {
        return (
            <Button type="primary">
                //Hook function and setTimmout Timer - > {Counter is: ${this. state. val} `}
            </Button>
        )
    }
}

export default SetState;

The second parameter of setState is a callback function.

If you want to get the latest values at setState, you can get the latest results in the callback function of setState.

this.setState(
    {
        data: newData
    },
    () => {
        console.log('Abreast of the times state value', me.state.data)
    }
);

summary

The so-called'asynchronism'of setState only exists in the life cycle function of synthetic event, and the original js time and timer change synchronously. The batch update optimization is also done in setState. If you want to get the latest state in time after this.setState({}), you can get it in its callback function.

More reading

Topics: Javascript React