setState 是同步还异步的?

发布于 2025-01-07 01:58:27 字数 4245 浏览 13 评论 0

可能是同步,也可能是异步

  1. 在事件绑定中是异步的
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>
      </>
    );
  }
}

还可以使用定时器

  1. 在定时器中是同步
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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

绾颜

暂无简介

文章
评论
27 人气
更多

推荐作者

闻呓

文章 0 评论 0

深府石板幽径

文章 0 评论 0

mabiao

文章 0 评论 0

枕花眠

文章 0 评论 0

qq_CrTt6n

文章 0 评论 0

红颜悴

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文