[React]: setState (Asynchronous)

React

11/05/2019


Previously on setState()

In [React]: Basics, we've seen that setState() is a asynchronous function which does not happen immediately, but rather, happens in the future. Consider below situation where a button updates a value of state.

JSX
class App extends React.Component {
constructor() {
super();
this.state = {
someVal: 33
};
}
}
render() {
return (
<div className="App">
<p>{this.state.someVal}</p>
<button onClick={this.handleClick}>Update state</button>
</div>
);
}
JSX
handleClick = () => {
this.setState({ someVal: this.state.someVal + 1 })
console.log(this.state.someVal)
}

Since setState() is asynchronous, the value doesn't get updated immediately on the console.

When using setState(), we give control to React library which figures out how to optimally update the state--React batches work into single update.

We've previously got around this by using a callback:

JSX
handleClick = () => {
this.setState({ someVal: this.state.someVal + 1 }, () =>
console.log(this.state.someVal)
)
}

However, this is a bad practice. Having multiple setState(), won't guarantee that the new state variable is the latest version.

Rule of thumb is not to use this.state inside setState(), but instead, create a function.

What you should do

JSX
handleClick = () => {
this.setState(
(prevState, prevProps) => {
return { someVal: prevState.someVal + 1 }
},
() => console.log(this.state.someVal)
)
}

This uses a function instead of an object, and using a parameter prevState will give the latest value for update.

When you somehow need to manipulate state after the update, add into second parameter where the callback function is.

How about prevProps?

Here is how to pass down props onto App in index.js

JSX
ReactDOM.render(<App increment={1} />, document.getElementById("root"))

Then, use call it with

JSX
handleClick = () => {
this.setState(
(prevState, prevProps) => {
return { someVal: prevState.someVal + prevProps.increment }
},
() => console.log(this.state.someVal)
)
}

Using props in constructor

You can also pass in props into constructor

JSX
constructor(props){
super(props)
this.state = {
someVal: 33
};
}

Allows to use props in constructor like

JSX
constructor(props){
super(props)
this.state = {
someVal: 33 + this.props.increment
};
}

By doing so, someVal will originally hold 35.

Alternate Class Syntax

Create React App allows Alternate Class Syntax which isn't allowed in the current JavaScript.

Example: Remove the constructor and just have state variable.

JSX
class App extends React.Component {
state = {
someVal: 33,
}
}

This initialize state as class field declaration.


WRITTEN BY

Keeping a record