Skip to content

Commit

Permalink
Docs: Clarification of setState() behavior (#9329)
Browse files Browse the repository at this point in the history
* Clarification of setState() behavior

`setState()` is a frequent source of confusion for people new to React, and I believe part of that is due to minimization of the impact of the asynchronous behavior of `setState()` in the documentation. This revision is an attempt to clarify that behavior. For motivation and justification, see [setState Gate](https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82).

* Update reference-react-component.md

* Signature fix

* Update to address @acdlite concerns

* Add more details
  • Loading branch information
ericelliott authored and gaearon committed Apr 13, 2017
1 parent 8e0150d commit d333bff
Showing 1 changed file with 44 additions and 11 deletions.
55 changes: 44 additions & 11 deletions docs/docs/reference-react-component.md
Original file line number Diff line number Diff line change
Expand Up @@ -229,34 +229,67 @@ componentWillUnmount()
### `setState()`

```javascript
setState(nextState, callback)
setState(updater, [callback])
```

Performs a shallow merge of nextState into current state. This is the primary method you use to trigger UI updates from event handlers and server request callbacks.
`setState()` enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.

The first argument can be an object (containing zero or more keys to update) or a function (of state and props) that returns an object containing keys to update.
Think of `setState()` as a *request* rather than an immediate command to update the component. For better perceived performance, React may delay it, and then update several components in a single pass. React does not guarantee that the state changes are applied immediately.

Here is the simple object usage:
`setState()` does not always immediately update the component. It may batch or defer the update until later. This makes reading `this.state` right after calling `setState()` a potential pitfall. Instead, use `componentDidUpdate` or a `setState` callback (`setState(updater, callback)`), either of which are guaranteed to fire after the update has been applied. If you need to set the state based on the previous state, read about the `updater` argument below.

`setState()` will always lead to a re-render unless `shouldComponentUpdate()` returns `false`. If mutable objects are being used and conditional rendering logic cannot be implemented in `shouldComponentUpdate()`, calling `setState()` only when the new state differs from the previous state will avoid unnecessary re-renders.

The first argument is an `updater` function with the signature:

```javascript
this.setState({mykey: 'my new value'});
(prevState, props) => nextState
```

It's also possible to pass a function with the signature `function(state, props) => newState`. This enqueues an atomic update that consults the previous value of state and props before setting any values. For instance, suppose we wanted to increment a value in state by `props.step`:
`prevState` is a reference to the previous state. It should not be directly mutated. Instead, changes should be represented by building a new state object based on the input from `prevState` and `props`. For instance, suppose we wanted to increment a value in state by `props.step`:

```javascript
this.setState((prevState, props) => {
return {myInteger: prevState.myInteger + props.step};
return {counter: prevState.counter + props.step};
});
```

The second parameter is an optional callback function that will be executed once `setState` is completed and the component is re-rendered. Generally we recommend using `componentDidUpdate()` for such logic instead.
Both `prevState` and `props` received by the updater function are guaranteed to be up-to-date.

`setState()` does not immediately mutate `this.state` but creates a pending state transition. Accessing `this.state` after calling this method can potentially return the existing value.
The second parameter to `setState()` is an optional callback function that will be executed once `setState` is completed and the component is re-rendered. Generally we recommend using `componentDidUpdate()` for such logic instead.

There is no guarantee of synchronous operation of calls to `setState` and calls may be batched for performance gains.
You may optionally pass an object as the first argument to `setState()` instead of a function:

`setState()` will always lead to a re-render unless `shouldComponentUpdate()` returns `false`. If mutable objects are being used and conditional rendering logic cannot be implemented in `shouldComponentUpdate()`, calling `setState()` only when the new state differs from the previous state will avoid unnecessary re-renders.
```javascript
setState(stateChange, [callback])
```

This performs a shallow merge of `stateChange` into the new state, e.g., to adjust a shopping cart item quantity:

```javascript
this.setState({quantity: 2})
```

This form of `setState()` is also asynchronous, and multiple calls during the same cycle may be batched together. For example, if you attempt to increment an item quantity more than once in the same cycle, that will result in the equivalent of:

```javaScript
Object.assign(
previousState,
{quantity: state.quantity + 1},
{quantity: state.quantity + 1},
...
)
```

Subsequent calls will override values from previous calls in the same cycle, so the quantity will only be incremented once. If the next state depends on the previous state, we recommend using the updater function form, instead:

```js
this.setState((prevState) => {
return {counter: prevState.quantity + 1};
});
```

For more detail, see the [State and Lifecycle guide](/react/docs/state-and-lifecycle.html).

* * *

Expand Down

0 comments on commit d333bff

Please sign in to comment.