Skip to content

Latest commit

 

History

History
76 lines (50 loc) · 4.69 KB

12.状态更新流程.md

File metadata and controls

76 lines (50 loc) · 4.69 KB

状态更新流程

课程地址 https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b36ecf10a4003b634724

在react中触发状态更新的几种方式:

ReactDOM.render this.setState this.forceUpdate useState useReducer

我们重点看下重点看下 this.setStatethis.forceUpdate

this.setState 内调用 this.updater.enqueueSetState ,主要是将 update 加入 updateQueueenqueueUpdate 用来将 update 加入 updateQueue 队列

React Class Component Queue

this.forceUpdatethis.setState 一样,只是会让 tag 赋值 ForceUpdate

如果标记 ForceUpdaterender阶段 组件更新会根据 checkHasForceUpdateAfterProcessing ,和 checkShouldComponentUpdate 来判断,如果 UpdatetagForceUpdate ,则 checkHasForceUpdateAfterProcessing 为true,当组件是 PureComponent 时,checkShouldComponentUpdate 会浅比较 stateprops,所以当使用 this.forceUpdate 一定会更新

状态更新整体流程 状态更新整体流程

Update&updateQueue

HostRoot 或者 ClassComponent 触发更新后,会在函数 createUpdate 中创建 update ,并在后面的 render 阶段的 beginWork 中计算 Update 。FunctionComponent 对应的 Update 在第11章讲,它和 HostRoot 或者 ClassComponentUpdate 结构有些不一样

我们主要关注这些参数:

  • lane:优先级
  • tag:更新的类型,例如 UpdateStateReplaceState
  • payloadClassComponentpayloadsetState 第一个参数,HostRootpayloadReactDOM.render 的第一个参数
  • callbacksetState 的第二个参数
  • next:连接下一个 Update 形成一个链表,例如同时触发多个 setState 时会形成多个 Update ,然后用 next 连接

对于 HostRoot 或者 ClassComponent 会在 mount 的时候使用 initializeUpdateQueue 创建 updateQueue ,然后将 updateQueue 挂载到 fiber 节点上

  • baseState:初始state,后面会基于这个state,根据 Update 计算新的state
  • firstBaseUpdatelastBaseUpdateUpdate 形成的链表的头和尾
  • shared.pending:新产生的 update 会以 单向环状链表 保存在 shared.pending 上,计算state的时候会剪开这个环状链表,并且链接在 lastBaseUpdate
  • effectscalback 不为null的 update

从触发更新的 fiber 节点向上遍历到 rootFibermarkUpdateLaneFromFiberToRoot 函数中会从触发更新的节点开始向上遍历到 rootFiber ,遍历的过程会处理节点的优先级

调度ensureRootIsScheduled 中,scheduleCallback 会以一个优先级调度 render阶段 的开始函数 performSyncWorkOnRoot 或者 performConcurrentWorkOnRoot

状态更新 classComponent 状态计算发生在 processUpdateQueue 函数中,涉及很多链表操作,看图更加直白

  • 初始时 fiber.updateQueue单链表 上有 firstBaseUpdate(update1)lastBaseUpdate(update2),以 next 连接
  • fiber.updateQueue.shared环状链表 上有 update3update4,以 next 连接互相连接
  • 计算state时,先将 fiber.updateQueue.shared环状链表‘剪开’,形成单链表,连接在 fiber.updateQueue 后面形成 baseUpdate
  • 然后遍历按这条链表,根据 baseState 计算出 memoizedState

classComponent 状态更新

带优先级的状态更新

  • 通过 ReactDOM.render 创建的应用没有优先级的概念
  • concurrent 模式下,类似git rebase,先暂存之前的代码,在master上开发,然后rebase到之前的分支上

优先级是由 Scheduler 来调度的,这里我们只关心状态计算时的优先级排序,也就是在函数 processUpdateQueue 中发生的计算,例如初始时有c1-c4四个update,其中c1和c3为高优先级

在第一次render的时候,低优先级的update 会跳过,所以只有c1和c3加入 状态的计算 在第二次render的时候,会以第一次中跳过的update(c2)之前的update(c1)作为baseState,跳过的update和之后的update(c2,c3,c4)作为 baseUpdate 重新计算 在在concurrent模式下,componentWillMount可能会执行多次,变现和之前的版本不一致

注意,fiber.updateQueue.shared 会同时存在于 workInprogress Fibercurrent Fiber ,目的是为了防止高优先级打断正在进行的计算而导致状态丢失,这段代码也是发生在 processUpdateQueue

concurrent setState