setState 是同步还异步的?
可能是同步,也可能是异步
- 在事件绑定中是异步的
class Counter extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } handleClick = () => { this.setState({ count: this.state.count + 1 }) console.log(this.state.count) // 0 this.setState({ count: this.state.count + 1 }) console.log(this.state.count) // 0 // 说明 setState 是异步的 } render() { return ( <> Count: {this.state.count} <button onClick={this.handleClick}>+</button> </> ); } } // 点击按钮,最后页面只加 1,但是代码设置了两次加法
如果新的 state 需要通过使用先前的 state 计算得出,那么可以将函数传递给 setState。
所以为了解决上面的问题,可以如下改写
class Counter extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } handleClick = () => { this.setState((prevState) => { return { count: prevState.count + 1 } }) this.setState((prevState) => { return { count: prevState.count + 1 } }) } render() { return ( <> Count: {this.state.count} <button onClick={this.handleClick}>+</button> </> ); } }
还可以使用定时器
- 在定时器中是同步
class Counter extends React.Component { constructor(props) { super(props) this.state = { count: 0 } } handleClick = () => { setTimeout(() => { this.setState({ count: this.state.count + 1 }) console.log(this.state.count) // 1 this.setState({ count: this.state.count + 1 }) console.log(this.state.count) // 2 // 说明 stateState 是同步的 }, 0); } handleClickFn = () => { this.setState((prevState) => { return { count: prevState.count + 1 } }) this.setState((prevState) => { return { count: prevState.count + 1 } }) } render() { return ( <> Count: {this.state.count} <button onClick={this.handleClick}>+</button> <button onClick={this.handleClickFn}>+</button> </> ); } }
注意这里的同步和异步指的是 setState 函数。因为涉及到 state 的状态合并,react 认为当你在事件绑定中操作 state 是非常频繁的,所以为了节约性能 react 会把多次 setState 进行合并为一次,最后在一次性的更新 state,而定时器里面操作 state 是不会把多次合并为一次更新的。
原理
有一个叫做事物的概念: Transaction
一个所谓的 Transaction 就是将需要执行的 method 使用 wrapper 封装起来,再通过 Transaction 提供的 perform 方法执行
在执行 perform 方法的过程中,先执行 initialize -> 再执行 anyMethod 需要的方法 -> 最后执行 close 方法,看到图片中那个长长的箭头了吗?
我们可以简单理解为通过事物,我们可以在方法开始之前执行一些操作,然后执行完目标方法,最后在方法执行完成之后再进行一些操作。
简单的代码模拟一下
function setState () { console.log('setState') } class Transaction { constructor (wrappers) { this.wrappers = wrappers } perform (func) { this.wrappers.forEach(wrapper => wrapper.initialize()) func() this.wrappers.forEach(wrapper => wrapper.close()) } } let transaction = new Transaction([ { initialize () { console.log('before1') }, close () { console.log('after1') } }, { initialize () { console.log('before2') }, close () { console.log('after2') } } ]) transaction.perform(setState)
然后结合到 setState 中使用
function setState () { console.log('setState') } class Transaction { constructor (wrappers) { this.wrappers = wrappers } perform (func) { this.wrappers.initialize() func() this.wrappers.close() } } let batchingStrategy = { isBatchingUpdates: false, updaters: [], batchedUpdates () { this.updaters.forEach(updater => { updater.component.updateComponent() }) } } let transaction = new Transaction({ initialize () { batchingStrategy.isBatchingUpdates = true }, close () { batchingStrategy.isBatchingUpdates = false; batchingStrategy.batchedUpdates(); } }) transaction.perform(setState)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

上一篇: 使用 effect 的注意事项
下一篇: 不要相信一个熬夜的人说的每一句话
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论