react-pits icon indicating copy to clipboard operation
react-pits copied to clipboard

React 中 setState() 是异步的

Open justjavac opened this issue 8 years ago • 12 comments

0x00

React has a setState() problem: Asking newbies to use setState() is a recipe for headaches. Advanced users have secret cures.

为了性能和其它原因,setState 这个 API 很容易被误用。

  • setState 不会立刻改变 React 组件中 state 的值
  • setState 通过引发一次组件的更新过程来引发重新绘制
  • 多次 setState 函数调用产生的效果会合并

0x01

问题

看如下代码:

// state.count 当前为 0
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});
this.setState({count: state.count + 1});
// state.count 现在是 1,而不是 3 

三次操作被合并为了一次。

0x02

解决方式

this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

参考资料

justjavac avatar Apr 04 '17 13:04 justjavac

setState 的第二个参数可以传一个回调函数,在 setState 生效之后调用

rccoder avatar Apr 04 '17 13:04 rccoder

@rccoder 是的

https://github.com/facebook/react/blob/c78464f8ea9a5b00ec80252d20a71a1482210e57/docs/_posts/2013-07-17-react-v0-4-0.md#L26

Updates to your component are batched now, which may result in a significantly faster re-render of components. this.setState now takes an optional callback as its second parameter. If you were using onClick={this.setState.bind(this, state)} previously, you'll want to make sure you add a third parameter so that the event is not treated as the callback.

v0.4.0

justjavac avatar Apr 04 '17 14:04 justjavac

image 应该是“误用”

mofelee avatar Apr 05 '17 10:04 mofelee

@MofeLee thx

justjavac avatar Apr 05 '17 13:04 justjavac

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.

为了性能和其它原因:

现在想知道为了哪些性能,以及其他哪些原因😏

mqliutie avatar Jul 07 '17 02:07 mqliutie

@mqliutie 多次 setState 会合并为一次 setState,所以提升了性能。设计成异步的,可以在调用 setState 时做其他操作。

justjavac avatar Jul 07 '17 03:07 justjavac

@mqliutie 将多次 setState() 合并可以减少 render() 执行次数.

iugo avatar Dec 15 '17 04:12 iugo

timer中多次setState是如何将多次更新合并为一次的呢?

Coooooooola avatar Oct 03 '18 10:10 Coooooooola

@mqliutie 将多次 setState() 合并可以减少 render() 执行次数. 错误,在timer中的setState并不能减少render次数

setTimeout(() => {
  this.setState({name})
  this.setState({name})
}, 500)

Coooooooola avatar Oct 04 '18 05:10 Coooooooola

@mqliutie 多次 setState 会合并为一次 setState,所以提升了性能。设计成异步的,可以在调用 setState 时做其他操作。

错误,只有在react自身的合成事件内调用的setState才会提升性能

Coooooooola avatar Oct 04 '18 05:10 Coooooooola

@mqliutie 将多次 setState() 合并可以减少 render() 执行次数. 错误,在timer中的setState并不能减少render次数

setTimeout(() => {
  this.setState({name})
  this.setState({name})
}, 500)

不是说一定减少渲染次数, 是在一定条件下, 比如官方举例 https://reactjs.org/docs/faq-state.html#when-is-setstate-asynchronous

在这个例子中: 如果上层与下层组件同时修改了自身的 state, 那么对于下层组件, props 变化了一次, state 变化了一次, 本会渲染两次, 但这时 react 会略微延迟让下层组件只渲染一次.

iugo avatar Oct 05 '18 03:10 iugo

我记得 Dan 曾在 Twitter 上说过, 这种异步机制具体如何优化性能, 团队还会进一步讨论与优化.

iugo avatar Oct 05 '18 03:10 iugo