updateClassComponent

开始是对context的操作,因为ClassComponent可以成为context provider

current === null只会出现在第一次渲染的时候,因为会先创建workInProcess,在渲染结束之后才会把workInProcess拷贝成current,代表着第一次渲染结束。而后面也会出现根据current === null来判断是否需要调用componentDidMount的代码

在这里如果current === null就行要进行实例的构建工作,如果不是,直接updateClassInstance

如果是还要判断实例是否已经创建workInProgress.stateNode === null,如果是的话要创建这个实例,通过constructClassInstance,并且挂载实例mountClassInstance

如果已经有current则调用updateClassInstance

最后调用finishClassComponent

function updateClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps,
  renderExpirationTime: ExpirationTime,
) {
  // Push context providers early to prevent context stack mismatches.
  // During mounting we don't know the child context yet as the instance doesn't exist.
  // We will invalidate the child context in finishClassComponent() right after rendering.
  let hasContext
  if (isLegacyContextProvider(Component)) {
    hasContext = true
    pushLegacyContextProvider(workInProgress)
  } else {
    hasContext = false
  }
  prepareToReadContext(workInProgress, renderExpirationTime)

  const instance = workInProgress.stateNode
  let shouldUpdate
  if (instance === null) {
    if (current !== null) {
      // An class component without an instance only mounts if it suspended
      // inside a non- concurrent tree, in an inconsistent state. We want to
      // tree it like a new mount, even though an empty version of it already
      // committed. Disconnect the alternate pointers.
      current.alternate = null
      workInProgress.alternate = null
      // Since this is conceptually a new fiber, schedule a Placement effect
      workInProgress.effectTag |= Placement
    }
    // In the initial pass we might need to construct the instance.
    constructClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    )
    mountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    )
    shouldUpdate = true
  } else if (current === null) {
    // In a resume, we'll already have an instance we can reuse.
    shouldUpdate = resumeMountClassInstance(
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    )
  } else {
    shouldUpdate = updateClassInstance(
      current,
      workInProgress,
      Component,
      nextProps,
      renderExpirationTime,
    )
  }
  return finishClassComponent(
    current,
    workInProgress,
    Component,
    shouldUpdate,
    hasContext,
    renderExpirationTime,
  )
}

constructClassInstance

首先根据workInProcess.type获取class,然后构建instance

function adoptClassInstance(workInProgress, instance) {
  instance.updater = classComponentUpdater
  workInProgress.stateNode = instance
  // The instance needs access to the fiber so that it can schedule updates
  set(instance, workInProgress)
  {
    instance._reactInternalInstance = fakeInternalInstance
  }
}

这个方法给 ClassComponent 的实例挂载一个updater对象

{
  enqueueForceUpdate: func,
  enqueueReplaceState: func,
  enqueueSetState: func,
  isMounted: func
}

很熟悉对不对,这几个方法就对应我们平时调用的forceUpdate, replaceState, setState

function set(key, value) {
  key._reactInternalFiber = value
}

这里给 instance 的_reactInternalFiber挂载上workInProgress,所以我们可以通过this._reactInternalFiber获取该实例对应的Fiber对象

关于Context这里

function constructClassInstance(
  workInProgress: Fiber,
  ctor: any,
  props: any,
  renderExpirationTime: ExpirationTime,
): any {
  const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true)
  const contextTypes = ctor.contextTypes
  const isContextConsumer = contextTypes !== null && contextTypes !== undefined
  const context = isContextConsumer
    ? getMaskedContext(workInProgress, unmaskedContext)
    : emptyContextObject

  const instance = new ctor(props, context)
  const state = (workInProgress.memoizedState =
    instance.state !== null && instance.state !== undefined
      ? instance.state
      : null)
  adoptClassInstance(workInProgress, instance)

  // 删了一堆关于开发时提醒不安全的生命周期方法的逻辑

  if (isContextConsumer) {
    cacheContext(workInProgress, unmaskedContext, context)
  }

  return instance
}

function adoptClassInstance(workInProgress: Fiber, instance: any): void {
  instance.updater = classComponentUpdater
  workInProgress.stateNode = instance
  // The instance needs access to the fiber so that it can schedule updates
  ReactInstanceMap.set(instance, workInProgress)
}

mountClassInstance

初始化 props、state 等实例属性,如果有updateQueue就更新之,一般来说第一次渲染是没有的。

processUpdateQueue用来计算新的state,详见这里

TODO: updateQueue

var getDerivedStateFromProps = workInProgress.type.getDerivedStateFromProps
if (typeof getDerivedStateFromProps === 'function') {
  applyDerivedStateFromProps(workInProgress, getDerivedStateFromProps, props)
  instance.state = workInProgress.memoizedState
}

如果 class 有声明getDerivedStateFromProps,就调用他,调用该方法之后会根据结果更新workInProgress.memoizedState,所以要重新赋值给instance.state

接下去判断一下是否没有新的生命周期方法,如果没有并且有componentWillMount生命周期方法,则调用他。

最后判断是否有componentDidMount,如果有就修改workInProgress.effectTag |= Update,因为componentDidMount要在真正渲染进DOM之后才调用,也就是commit之后

function mountClassInstance(
  workInProgress: Fiber,
  ctor: any,
  newProps: any,
  renderExpirationTime: ExpirationTime,
): void {
  if (__DEV__) {
    checkClassInstance(workInProgress, ctor, newProps)
  }

  const instance = workInProgress.stateNode
  const unmaskedContext = getUnmaskedContext(workInProgress, ctor, true)

  instance.props = newProps
  instance.state = workInProgress.memoizedState
  instance.refs = emptyRefsObject
  instance.context = getMaskedContext(workInProgress, unmaskedContext)

  let updateQueue = workInProgress.updateQueue
  if (updateQueue !== null) {
    processUpdateQueue(
      workInProgress,
      updateQueue,
      newProps,
      instance,
      renderExpirationTime,
    )
    instance.state = workInProgress.memoizedState
  }

  const getDerivedStateFromProps = ctor.getDerivedStateFromProps
  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      ctor,
      getDerivedStateFromProps,
      newProps,
    )
    instance.state = workInProgress.memoizedState
  }

  if (
    typeof ctor.getDerivedStateFromProps !== 'function' &&
    typeof instance.getSnapshotBeforeUpdate !== 'function' &&
    (typeof instance.UNSAFE_componentWillMount === 'function' ||
      typeof instance.componentWillMount === 'function')
  ) {
    callComponentWillMount(workInProgress, instance)
    // If we had additional state updates during this life-cycle, let's
    // process them now.
    updateQueue = workInProgress.updateQueue
    if (updateQueue !== null) {
      processUpdateQueue(
        workInProgress,
        updateQueue,
        newProps,
        instance,
        renderExpirationTime,
      )
      instance.state = workInProgress.memoizedState
    }
  }

  if (typeof instance.componentDidMount === 'function') {
    workInProgress.effectTag |= Update
  }
}

applyDerivedStateFromProps

调用getDerivedStateFromProps获得最新的state并放到fiber.memoizedState

export function applyDerivedStateFromProps(
  workInProgress: Fiber,
  getDerivedStateFromProps: (props: any, state: any) => any,
  nextProps: any,
) {
  const prevState = workInProgress.memoizedState

  const partialState = getDerivedStateFromProps(nextProps, prevState)

  // Merge the partial state and the previous state.
  const memoizedState =
    partialState === null || partialState === undefined
      ? prevState
      : Object.assign({}, prevState, partialState)
  workInProgress.memoizedState = memoizedState

  // Once the update queue is empty, persist the derived state onto the
  // base state.
  const updateQueue = workInProgress.updateQueue
  if (updateQueue !== null && updateQueue.expirationTime === NoWork) {
    updateQueue.baseState = memoizedState
  }
}

finishClassComponent

首先markRef

function markRef(current: Fiber | null, workInProgress: Fiber) {
  const ref = workInProgress.ref
  if (
    (current === null && ref !== null) ||
    (current !== null && current.ref !== ref)
  ) {
    // Schedule a Ref effect
    workInProgress.effectTag |= Ref
  }
}

也就是给effectTag加上Ref属性

然后如果即shouldUpdate不为true,又没有捕获到错误,那么说明这个组件已经更新完成了,直接返回bailoutOnAlreadyFinishedWork

之后进入render阶段,这个阶段主要是获取instance.render的结果,作为当前Fiberchildren,到这里就结束了render阶段。

再给effectTag加上PerformedWork,接下去就进入了日常的reconcileChildrenAtExpirationTime阶段了

function finishClassComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  shouldUpdate: boolean,
  hasContext: boolean,
  renderExpirationTime: ExpirationTime,
) {
  // Refs should update even if shouldComponentUpdate returns false
  markRef(current, workInProgress)

  const didCaptureError = (workInProgress.effectTag & DidCapture) !== NoEffect

  if (!shouldUpdate && !didCaptureError) {
    // Context providers should defer to sCU for rendering
    if (hasContext) {
      invalidateContextProvider(workInProgress, false)
    }

    return bailoutOnAlreadyFinishedWork(current, workInProgress)
  }

  const ctor = workInProgress.type
  const instance = workInProgress.stateNode

  // Rerender
  ReactCurrentOwner.current = workInProgress
  let nextChildren
  if (
    didCaptureError &&
    (!enableGetDerivedStateFromCatch ||
      typeof ctor.getDerivedStateFromCatch !== 'function')
  ) {
    nextChildren = null

    if (enableProfilerTimer) {
      stopBaseRenderTimerIfRunning()
    }
  } else {
    nextChildren = instance.render()
  }

  // React DevTools reads this flag.
  workInProgress.effectTag |= PerformedWork
  if (didCaptureError) {
    reconcileChildrenAtExpirationTime(
      current,
      workInProgress,
      null,
      renderExpirationTime,
    )
    workInProgress.child = null
  }
  reconcileChildrenAtExpirationTime(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  )
  // Memoize props and state using the values we just used to render.
  // TODO: Restructure so we never read values from the instance.
  memoizeState(workInProgress, instance.state)
  memoizeProps(workInProgress, instance.props)

  // The context might have changed so we need to recalculate it.
  if (hasContext) {
    invalidateContextProvider(workInProgress, true)
  }

  return workInProgress.child
}

updateClassInstance

这个代码就是按照流程来办事来,比较简单就不详细讲了。主要就是调用来更新相关的几个生命周期方法

checkShouldComponentUpdate里面会调用shouldComponentUpdate生命周期方法

这里看出来是否需要update只取决于你shouldComponentUpdate的返回以及是否是PureComponent并且propsstate没有变化

function checkShouldComponentUpdate(
  workInProgress,
  ctor,
  oldProps,
  newProps,
  oldState,
  newState,
  nextLegacyContext,
) {
  const instance = workInProgress.stateNode
  if (typeof instance.shouldComponentUpdate === 'function') {
    startPhaseTimer(workInProgress, 'shouldComponentUpdate')
    const shouldUpdate = instance.shouldComponentUpdate(
      newProps,
      newState,
      nextLegacyContext,
    )
    stopPhaseTimer()

    if (__DEV__) {
      warningWithoutStack(
        shouldUpdate !== undefined,
        '%s.shouldComponentUpdate(): Returned undefined instead of a ' +
          'boolean value. Make sure to return true or false.',
        getComponentName(ctor) || 'Component',
      )
    }

    return shouldUpdate
  }

  if (ctor.prototype && ctor.prototype.isPureReactComponent) {
    return (
      !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)
    )
  }

  return true
}
function updateClassInstance(
  current: Fiber,
  workInProgress: Fiber,
  renderExpirationTime: ExpirationTime,
): boolean {
  const ctor = workInProgress.type
  const instance = workInProgress.stateNode

  const oldProps = workInProgress.memoizedProps
  const newProps = workInProgress.pendingProps
  instance.props = oldProps

  const oldContext = instance.context
  const newUnmaskedContext = getUnmaskedContext(workInProgress)
  const newContext = getMaskedContext(workInProgress, newUnmaskedContext)

  const getDerivedStateFromProps = ctor.getDerivedStateFromProps
  const hasNewLifecycles =
    typeof getDerivedStateFromProps === 'function' ||
    typeof instance.getSnapshotBeforeUpdate === 'function'

  if (
    !hasNewLifecycles &&
    (typeof instance.UNSAFE_componentWillReceiveProps === 'function' ||
      typeof instance.componentWillReceiveProps === 'function')
  ) {
    if (oldProps !== newProps || oldContext !== newContext) {
      callComponentWillReceiveProps(
        workInProgress,
        instance,
        newProps,
        newContext,
      )
    }
  }

  resetHasForceUpdateBeforeProcessing()

  const oldState = workInProgress.memoizedState
  let newState = (instance.state = oldState)
  let updateQueue = workInProgress.updateQueue
  if (updateQueue !== null) {
    processUpdateQueue(
      workInProgress,
      updateQueue,
      newProps,
      instance,
      renderExpirationTime,
    )
    newState = workInProgress.memoizedState
  }

  if (
    oldProps === newProps &&
    oldState === newState &&
    !hasContextChanged() &&
    !checkHasForceUpdateAfterProcessing()
  ) {
    if (typeof instance.componentDidUpdate === 'function') {
      if (
        oldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Update
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if (
        oldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Snapshot
      }
    }
    return false
  }

  if (typeof getDerivedStateFromProps === 'function') {
    applyDerivedStateFromProps(
      workInProgress,
      getDerivedStateFromProps,
      newProps,
    )
    newState = workInProgress.memoizedState
  }

  const shouldUpdate =
    checkHasForceUpdateAfterProcessing() ||
    checkShouldComponentUpdate(
      workInProgress,
      oldProps,
      newProps,
      oldState,
      newState,
      newContext,
    )

  if (shouldUpdate) {
    // In order to support react-lifecycles-compat polyfilled components,
    // Unsafe lifecycles should not be invoked for components using the new APIs.
    if (
      !hasNewLifecycles &&
      (typeof instance.UNSAFE_componentWillUpdate === 'function' ||
        typeof instance.componentWillUpdate === 'function')
    ) {
      startPhaseTimer(workInProgress, 'componentWillUpdate')
      if (typeof instance.componentWillUpdate === 'function') {
        instance.componentWillUpdate(newProps, newState, newContext)
      }
      if (typeof instance.UNSAFE_componentWillUpdate === 'function') {
        instance.UNSAFE_componentWillUpdate(newProps, newState, newContext)
      }
      stopPhaseTimer()
    }
    if (typeof instance.componentDidUpdate === 'function') {
      workInProgress.effectTag |= Update
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      workInProgress.effectTag |= Snapshot
    }
  } else {
    // If an update was already in progress, we should schedule an Update
    // effect even though we're bailing out, so that cWU/cDU are called.
    if (typeof instance.componentDidUpdate === 'function') {
      if (
        oldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Update
      }
    }
    if (typeof instance.getSnapshotBeforeUpdate === 'function') {
      if (
        oldProps !== current.memoizedProps ||
        oldState !== current.memoizedState
      ) {
        workInProgress.effectTag |= Snapshot
      }
    }

    // If shouldComponentUpdate returned false, we should still update the
    // memoized props/state to indicate that this work can be reused.
    workInProgress.memoizedProps = newProps
    workInProgress.memoizedState = newState
  }

  // Update the existing instance's state, props, and context pointers even
  // if shouldComponentUpdate returns false.
  instance.props = newProps
  instance.state = newState
  instance.context = newContext

  return shouldUpdate
}

results matching ""

    No results matching ""

    Jokcy的二维码

    扫码添加Jokcy,更多更新更优质的前端学习内容不断更新中,期待与你一起成长!

    Jokcy的二维码