The environment

The React 16.9.0 React – Dom 16.9.0

preface

The following conclusions can be drawn from the results of the following code:

  • With setTimeout and native events, update results are immediately available. That’s synchronization
  • In composite events and life cycles, update results are not available immediately. It’s called asynchronous.
  • In composite events and life cycles, if the same value is evaluated multiple timessetState.setStateThe batch update policy overwrites it and takes the last execution
class App extends React.Component { constructor () { super(); this.state = { counter: 0 }; } componentDidMount() {console.log("componentDidMount before: "+ this.state.counter); this.setState({ counter: this.state.counter + 1 }); // Console. log("componentDidMount After: "+ this.state.counter); SetTimeout (() => {// setTimeout call console.log("setTimeout before: "+ this.state.counter); this.setState({ counter: this.state.counter + 1 }); console.log("setTimeout after: " + this.state.counter); }, 0); document.getElementById("btn-2").addEventListener("click", this.btn2Click); } spanClick = () => { const { counter } = this.state; console.log("spanClick before: " + this.state.counter); this.setState({ counter: counter + 1 }) this.setState({ counter: Counter + 2}) // Console. log("spanClick after: "+ this.state.counter); } btn2Click = () => { const { counter } = this.state; console.log("addEventListener btn2Click before: " + this.state.counter); This.setstate ({counter: counter + 1}) // Console. log("addEventListener btn2Click after: " + this.state.counter); } render () {return (<div className="App"> <span className=" BTN "onClick={(event) => this.spanclick (event)}> click < / span > < span the className = "BTN - 2" id = "BTN - 2" > click 2 < / span > < / div >)}; } // Print the result. // componentDidMount before: 0 // componentDidMount after: 0 // componentDidMount before: 0 1 // setTimeout after: 2 // spanClick before: 2 // spanClick after: 2 // addEventListener btn2Click before: 4 // addEventListener btn2Click after: 5Copy the code

I have seen many articles with such titles, and I have seen many conclusions with different descriptions but meanings as above, but I still have some doubts:

  • Why do setTimeout and native events update synchronously?
  • When exactly did the “asynchronous” situation update state?

Why do setTimeout and native events update synchronously

Whether the update is synchronous or asynchronous depends on the environment in which the code is being executed. React defines an internal variable, executionContext(NoContext by default), which is used for compositing events and lifecycle processing. This variable will first be assigned to DiscreteEventContext(composed event) or executionContext &= ~BatchedContext; executionContext |= LegacyUnbatchedContext; (componentDidMount). To mark the execution environment in which it is now operating.

In setTimeout and native events, there’s no executionContext, executionContext is the default NoContext; . Below is a screenshot of native event execution

When scheduleWork processes logic, if the execution environment is not NoContext, it simply puts the update in a queue without actually using it (i.e., calling flushSyncCallbackQueue).

conclusion

Whether or not the update is synchronous depends on the execution environment. Because setTimeout and native events are removed from the original execution environment, their state is updated synchronously.

When is state updated in the “asynchronous” scenario

When is flushSyncCallbackQueue called and what is the output from the following code in the synthesized event and life cycle?

class App extends React.Component { constructor () { super(); this.state = { counter: 0 }; } appClick = () => { console.log('--------appClick---------'); const { counter } = this.state; console.log(this.state.counter); this.setState({ counter: counter + 1 }) console.log(this.state.counter); // Will output 2 or 0? } spanClick = () => { const { counter } = this.state; this.setState({ counter: counter + 1 }) console.log(this.state.counter); this.setState({ counter: counter + 2 }) console.log(this.state.counter); } render () { return ( <div className="App" onClick={(event) => this.appClick(event)}> <span className="btn" The onClick = {(event) = > enclosing spanClick (event)} > click < / span > < / div >)}; }Copy the code

If you’re familiar with React native events, use the Click event as an example. React puts all callbacks in a queue when a click is processed.

Reference juejin. Cn/post / 684490…

FlushSyncCallbackQueue is called after the queue completes execution. So in the example code above, all prints are 0;

The execution process can be expressed simply as follows:

var a = 1; var updateQueue = []; function setState (payload) { updateQueue.push(payload); } function func () { updateQueue = []; Try {// put together setState({a: 1}); // output 1 console.log(window.a); setState({a: 3}); // output 1 console.log(window.a); } finally {// Create an update window.a = updatequue. Reduce ((Accumulator, currentValue) => { return currentValue.a || accumulator; }, window.a)}} // Run func() // print 3 console.log(window.a);Copy the code

conclusion

Wrap a layer of try {// execution around life cycle or composition events, update the queue} finally {// update state}, and finally update state in finally