# 8.setState:是异步还是同步

详见: seState 是异步还是同步 (opens new window) useState 和 setState (opens new window)

# 结论:

  1. 在正常的 react 的事件流里(如 onClick 等)
  • setState 和 useState 是异步执行的(不会立即更新 state 的结果)
  • 多次执行 setState 和 useState,只会调用一次重新渲染 render
  • 不同的是,setState 会进行 state 的合并,而 useState 则不会
  1. 在 setTimeout,Promise.then 等异步事件中
  • setState 和 useState 是同步执行的(立即更新 state 的结果)
  • 多次执行 setState 和 useState,每一次的执行 setState 和 useState,都会调用一次 render
  1. 原因是:react 会整合多个状态合并更新,减少 re-render 调用

  2. 只要你进入了 react 的调度流程,那就是异步的。只要你没有进入 react 的调度流程,那就是同步的。什么东西不会进入 react 的调度流程? setTimeout setInterval ,直接在 DOM 上绑定原生事件等。这些都不会走 React 的调度流程,你在这种情况下调用 setState ,那这次 setState 就是同步的。 否则就是异步的。

  3. this.setState 和 useState 有所不同

    1. 同步执行时 useState 也会对 state 进行逐个处理,而 setState 则只会处理最后一次 如
class Component extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      a: 1,
    }
  }

  handleClickWithPromise = () => {
    Promise.resolve().then(() => {
      this.setState({a: this.state.a + 1})
      this.setState({a: this.state.a + 1})
    })
  }

  handleClickWithoutPromise = () => {
    this.setState({a: this.state.a + 1})
    this.setState({a: this.state.a + 1})
  }

  render() {
    console.log('a', this.state.a)
    return (
      <Fragment>
        <button onClick={this.handleClickWithPromise}>异步执行</button>
        <button onClick={this.handleClickWithoutPromise}>同步执行</button>
      </Fragment>
    )
  }
}
//当点击同步执行按钮时,两次 setState 合并,只执行了最后一次,打印 2
// 当点击异步执行按钮时,两次 setState 各自 render 一次,分别打印 2,3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
function Component() {
  const [a, setA] = useState(1)
  console.log('a', a)

  function handleClickWithPromise() {
    Promise.resolve().then(() => {
      setA((a) => a + 1)
      setA((a) => a + 1)
    })
  }

  function handleClickWithoutPromise() {
    setA((a) => a + 1)
    setA((a) => a + 1)
  }

  return (
    <Fragment>
      <button onClick={handleClickWithPromise}>{a} 异步执行</button>
      <button onClick={handleClickWithoutPromise}>{a} 同步执行</button>
    </Fragment>
  )
// 当点击同步执行按钮时,两次 setA 都执行,但合并 render 了一次,打印 3
// 当点击异步执行按钮时,两次 setA 各自 render 一次,分别打印 2,3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Last Updated: 6/3/2024, 1:08:34 AM