# setState

setState是同步的还是异步的?在什么场景下是同步?什么场景下是异步?

# 特点一:浅合并

this.setState({ comments }) 完整保留了 this.state.posts,但完全替换换了 this.state.comments

# 特点二:批量更新策略

# 现象

在合成事件(或生命周期钩子函数)中,对同一个state基于上一次的值多次setState,只有一次的效果

class App extends Component {

  state = { val: 0 }

  batchUpdates = () => {
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
    this.setState({ val: this.state.val + 1 })
 }

  render() {
    return (
      <div onClick={this.batchUpdates}>
        {`Counter is ${this.state.val}`} // 1
      </div>
    )
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 更新机制

当调用setState时:

  • newState 放入到 当前组件实例 下的更新队列

  • 判断:是否处于 批量更新 的过程

    • 若是,把 组件实例 push到 dirtyComponents 中,等待更新
  • 否则,进行 批量更新,设置 状态标识位,调用 transaction.perform()

  • 遍历dirtyComponents,调用updateComponent

  • 事务结束时,会将 所有临时state 合并、计算出最新state(flushBatchedUpdates),调用生命周期方法来更新组件,isBatchingUpdates = false

alt

TIP

事务(Transaction) 用 Wrapper 封装要执行的方法,暴露一个perform方法来调用原方法。

在Wrapper里定义 initializeclose 方法:它们 分别 会在 指定方法 执行前、执行后执行。

* <pre>
 *                       wrappers (injected at creation time)
 *                                      +        +
 *                                      |        |
 *                    +-----------------|--------|--------------+
 *                    |                 v        |              |
 *                    |      +---------------+   |              |
 *                    |   +--|    wrapper1   |---|----+         |
 *                    |   |  +---------------+   v    |         |
 *                    |   |          +-------------+  |         |
 *                    |   |     +----|   wrapper2  |--------+   |
 *                    |   |     |    +-------------+  |     |   |
 *                    |   |     |                     |     |   |
 *                    |   v     v                     v     v   | wrapper
 *                    | +---+ +---+   +---------+   +---+ +---+ | invariants
 * perform(anyMethod) | |   | |   |   |         |   |   | |   | | maintained
 * +----------------->|-|---|-|---|-->|anyMethod|---|---|-|---|-|-------->
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | |   | |   |   |         |   |   | |   | |
 *                    | +---+ +---+   +---------+   +---+ +---+ |
 *                    |  initialize                    close    |
 *                    +-----------------------------------------+
 * </pre>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 总结

类型 方式 说明
合成事件
生命周期函数
(除了componentDidUpdate
会触发一个大事务 异步
原生事件
setTimeout
执行时 不存在大事务,会立即发起新的批量更新 同步
  • 为什么state要批量更新?

    • 避免不必要的重新渲染,从而提升性能。
  • setState是异步还是同步的?

    • 同步的。但有时(合成事件、生命周期)表现出来是异步。
  • 为什么在setTimeout方法中调用setState表现出来是同步?

    • 因为setTimeout已经完成了原组件的更新流程,不会放入dirtyComponents
  • setState中传入一个Function,为何里面的state值是最新的?

    • 函数接收的state是上轮更新过的state。
    this.setState(prevState => ({ count: prevState.count + 1 }))
    
    1

# 参考链接

更新时间: 5/7/2020, 3:41:24 PM