Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

【React】setState的执行机制 #60

Open
Tracked by #6
swiftwind0405 opened this issue Jul 10, 2020 · 1 comment
Open
Tracked by #6

【React】setState的执行机制 #60

swiftwind0405 opened this issue Jul 10, 2020 · 1 comment
Labels

Comments

@swiftwind0405
Copy link
Owner

swiftwind0405 commented Jul 10, 2020

先来说一下 setState 的认知:

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

重绘指的就是引起 React 的更新生命周期函数4个函数:

  • shouldComponentUpdate(被调用时this.state没有更新;如果返回了false,生命周期被中断,虽然不调用之后的函数了,但是state仍然会被更新)
  • componentWillUpdate(被调用时this.state没有更新)
  • render(被调用时this.state得到更新)
  • componentDidUpdate
    如果每一次 setState 调用都走一圈生命周期,光是想一想也会觉得会带来性能的问题,其实这四个函数都是纯函数,性能应该还好,但是 render 函数返回的结果会拿去做 Virtual DOM 比较和更新 DOM 树,这个就比较费时间。

目前React会将setState的效果放在队列中,积攒着一次引发更新过程。

为的就是把 Virtual DOM 和 DOM 树操作降到最小,用于提高性能。

setState 什么时候会执行同步更新?

在React中,如果是由React引发的事件处理(比如通过onClick引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的setState调用会同步执行this.state。

所谓“除此之外”,指的是绕过React通过 addEventListener 直接添加的事件处理函数,还有通过setTimeout || setInterval 产生的异步调用。

简单一点说, 就是经过React 处理的事件是不会同步更新 this.state的. 通过 addEventListener || setTimeout/setInterval 的方式处理的则会同步更新。

image

在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列 中。
isBatchingUpdates 默认是 false,也就表示 setState 会同步更新 this.state,但是有一个函数 batchedUpdates
这个函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会调用这个 batchedUpdates,造成的后果,就是由 React 控制的事件处理过程 setState 不会同步更新 this.state

setState执行过程

1. 流程图

image

  • partialStatesetState 传入的第一个参数,对象或函数
  • _pendingStateQueue:当前组件等待执行更新的 state 队列
  • isBatchingUpdates:react 用于标识当前是否处于批量更新状态,所有组件公用
  • dirtyComponent:当前所有处于待更新状态的组件队列
  • transcation:react 的事务机制,在被事务调用的方法外包装 n 个 waper 对象,并一次执行: waper.init、被调用方法、 waper.close
  • FLUSH_BATCHED_UPDATES:用于执行更新的 waper,只有一个 close 方法

2. 执行过程

对照上面流程图的文字说明,大概可分为以下几步:

  1. setState 传入的 partialState 参数存储在当前组件实例的 state 暂存队列中。
  2. 判断当前 React 是否处于批量更新状态,如果是,将当前组件加入待更新的组件队列中。
  3. 如果未处于批量更新状态,将批量更新状态标识设置为 true,用事务再次调用前一步方法,保证当前组件加入到了待更新组件队列中。
  4. 调用事务的 waper 方法,遍历待更新组件队列依次执行更新。
  5. 执行生命周期 componentWillReceiveProps
  6. 将组件的 state 暂存队列中的 state 进行合并,获得最终要更新的 state 对象,并将队列置为空。
  7. 执行生命周期 componentShouldUpdate,根据返回值判断是否要继续更新。
  8. 执行生命周期 componentWillUpdate
  9. 执行真正的更新, render
  10. 执行生命周期 componentDidUpdate

由执行机制看, setState本身并不是异步的,而是如果在调用 setState时,如果 react正处于更新过程,当前更新会被暂存,等上一次更新执行后在执行,这个过程给人一种异步的假象。

参考文档

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant