The author: Fried cheese

The characteristics of the setState

setState(updater, [callback]) / / class
const [value, setValue] = useState(init) / / the hooks
Copy the code
  1. SetState /setValue queues the latest state and notifies React that there is an update and the parent and child components need to be rerendered
  2. The state changes caused by the above two methods will not update components in time (synchronous), but batch and asynchronous update components. Therefore, it is highly likely that the latest state value cannot be obtained after calling setState (same as setValue)
  3. For the problem in 2, if setState is used, the state change can be processed in callback or componentDidUpdate; if useState is used, the state change can be subscribed in useEffect and processed accordingly

Note: The “asynchronous” of setState does not mean that it is internally implemented by asynchronous code, in fact, the process and code itself are synchronous, but the call order of the synthesized event and hook function is before the update, so that the updated value cannot be immediately obtained in the synthesized event and hook function, which forms the so-called “asynchronous”. Of course, the callback in the second parameter setState(partialState, callback) can get the updated result.

Why and when is setState batched?

Why batch processing

Consider that from the React user’s point of view, there is a significant performance impact if every state update causes a component to be re-rendered. So batch processing of state updates is inevitable.

From the React developer’s point of view, if every update

  • This can break the consistency between props and state, causing problems that are difficult to debug.
  • This would make some of the new features we were implementing impossible to implement.

When batch processing occurs

In React16 and earlier, React batched all updates to event listeners triggered by React, such as the onClick function. If you bypass React components such as addEventListenr, or make asynchronous calls such as asynchronous requests or setTimeout, etc. No batch processing takes place

React will batch all updates in version 17 and later

If we wanted to batch non-react internal triggering functions, we would do the following

Promise().then(() = > {
  ReactDOM.unstable_batchedUpdates(() = > {
  this.setState({ k: 1 })
  this.setState({ x: 2})})})Copy the code

Principle: In the React setState function, a variable isBatchingUpdates is used to determine whether the current change is to update the state directly or to queue it. IsBatchingUpdates is false by default. BachedUpdates will be called first, setting isBatchingUpdates to true, and all updates will be queued until the function ends, at which point the state will be updated

How do I get the latest state value?

The latest state cannot be obtained due to batch processing, as shown below

import React, { Component } from "react";
class App extends Component { 
  state = { title: 0 };

  componentDidMount() {}

  handleClick = () = > {
     / / batch
     this.setState({ title: this.state.title + 1 });
     this.setState({ title: this.state.title + 1 })
    console.log(this.state.title) / / 0
  };

  render() {
    const { title } = this.state;
    console.log('render'.this.state.title) // 'render', 1
    return ( <div id="id" onClick={this.handleClick}> {title} </div>); }}export default App; 

Copy the code

Option 1: Pass a function to setState to access the current state

handleClick = () = > { 
  // setState When the first parameter is function, the function parameters are prevState and prevProps respectively, and the latest state can be obtained
  this.setState((prevState) = > ({ title: prevState.title + 1 })); 
  this.setState((prevState) = > ({ title: prevState.title + 1 })); 
}

render () {
  console.log('render'.this.state.title) / / 2
  return <div id="id" onClick={this.handleClick}>{title}</div>
} 
Copy the code

2. Scenario 2: Use the second parameter of setState, the callback function, to get the latest state

// The callback function gets the latest state
 handleClick = () = > {
    this.setState({ title: this.state.title + 1 }, () = > {
      console.log(this.state.title) / / 1
    })
    console.log(this.state.title) / / 0
} 

render() {
  console.log('render'.this.state.title) / / 1
  return ( <div id="id" onClick={this.handleClick}> {title} </div> ); 
} 
Copy the code

Note: The callback function is called after setState completes and render executes

handleClick = () = > {
  this.setState( (prevState) = > ({ title: prevState.title + 1 }), () = > {
    console.log("1".this.state); / / "1", 2});this.setState( (prevState) = > ({ title: prevState.title + 1 }), () = > { 
    console.log("2".this.state); / / "2", 2}); }render() {
    console.log('render'.this.state.title) / / 2
    return ( <div id="id" onClick={this.handleClick}> {title} </div> ); 
} 
Copy the code

The above contains some personal understanding, if not correct, please point out, thank you!

Reference Documents:

Github.com/sisterAn/bl…

React