Skip to content

Latest commit

 

History

History
122 lines (97 loc) · 3.98 KB

re-render.md

File metadata and controls

122 lines (97 loc) · 3.98 KB

源码解析二十七 hookre-render

如果当前FunctionComponent正在render时,我们触发了dispatch。则会在当前render结束后在重新执行一次render,完全没必要再重新走一次完整的dispatch链路,这个过程我们称为re-render

那么这一切是如何实现的呢,首先,我们生成一个map用来存储这些需要re-render时需要的update,映射queue与任务队列。在dispatch中的逻辑如下

function dispatchAction<S, A>(fiber: Fiber, queue: UpdateQueue<S, A>, action: A) {
  const { alternate } = fiber

  // 当前处于render状态
  if (fiber === currentlyRenderingFiber || (alternate !== null && alternate === currentlyRenderingFiber)) {
    didScheduleRenderPhaseUpdate = true // 触发re-render
    const update: Update<S, A> = {
      expirationTime: renderExpirationTime,
      action,
      eagerReducer: null,
      eagerState: null,
      next: null,
    }

    // 生成 queue 的映射队列
    if (renderPhaseUpdates === null) {
      renderPhaseUpdates = new Map()
    }

    // 放入 map
    const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue)
    if (firstRenderPhaseUpdate === undefined) {
      renderPhaseUpdates.set(queue, update)
    } else {
      let lastRenderPhaseUpdate = firstRenderPhaseUpdate
      while (lastRenderPhaseUpdate.next !== null) {
        lastRenderPhaseUpdate = lastRenderPhaseUpdate.next
      }
      lastRenderPhaseUpdate.next = update
    }
  } else {
    ...
  }
}

renderWithHooks中,判断didScheduleRenderPhaseUpdate是否需要触发re-render,如果需要re-render的话会把相应的全局变量重置,并再执行一次component()

function renderWithHooks(current: Fiber, workInProgress: Fiber, Component: Function, props: any, refOrContext: any, nextRenderExpirationTime: ExpirationTime): any {
  renderExpirationTime = nextRenderExpirationTime
  currentlyRenderingFiber = workInProgress
  nextCurrentHook = current !== null ? current.memoizedState : null

  ReactCurrentDispatcher.current = nextCurrentHook === null ? HooksDispatcherOnMount : HooksDispatcherOnUpdate

  let children: any = Component(props, refOrContext)

  if (didScheduleRenderPhaseUpdate) {
    do {
      didScheduleRenderPhaseUpdate = false
      numberOfReRenders += 1

      // 从链表头开始
      nextCurrentHook = current !== null ? current.memoizedState : null
      nextWorkInProgressHook = firstWorkInProgressHook

      currentHook = null
      workInProgressHook = null
      componentUpdateQueue = null

      children = Component(props, refOrContext)
    } while (didScheduleRenderPhaseUpdate)

    renderPhaseUpdates = null
    numberOfReRenders = 0
  }
  ...
}

最后,在updateReducer中,由于普通的update我们在第一次render中已全部处理过,所以我们只需要针对map中的update进行处理就好

updateReducer<S, A>(reducer: (s: S, a: A) => S): [S, Dispatch<A>] {
  const hook = updateWorkInProgressHook()
  const { queue } = hook

  if (numberOfReRenders > 0) { // 处于re-render状态
    const { dispatch: _dispatch } = queue

    if (renderPhaseUpdates !== null) {
      const firstRenderPhaseUpdate = renderPhaseUpdates.get(queue)
      if (firstRenderPhaseUpdate !== undefined) {
        renderPhaseUpdates.delete(queue)

        let newState = hook.memoizedState
        let update = firstRenderPhaseUpdate
        do {
          const { action } = update
          newState = reducer(newState, action)
          update = update.next
        } while (update !== null)

        if (!Object.is(newState, hook.memoizedState)) { // 标记为更新
          markWorkInProgressReceivedUpdate()
        }

        hook.memoizedState = newState

        if (hook.baseUpdate === queue.last) {
          hook.baseState = newState
        }

        queue.eagerReducer = reducer
        queue.eagerState = newState

        return [newState, _dispatch]
      }
    }

    return [hook.memoizedState, _dispatch]
  }
  ...
}