React源码解析9-其他组件更新

1.portal组件更新

1.api返回的$$type为portal 这个和reactElement处于同等级

ReactDOM.createPortal直接返回的一个对象结构就有children

 return {
    // This tag allow us to uniquely identify this as a React Portal
    $$typeof: REACT_PORTAL_TYPE,
    key: key == null ? null : '' + key,
    children,
    containerInfo,
    implementation,
  };
复制代码

在最开始的创建fiber树阶段 这个节点就是个fiber 不过tag=5 children就是传进来的children 和之前一样 只是在commit阶段会把这个节点挂载到别的地方去

{this.state.show ? (
          <p class='ta'>{ReactDOM.createPortal(<span>123</span>, portalElm)}</p>
        ) : null}
复制代码

p标签的props如下

image-20210507175956748

证明ReactDOM.createPortal函数返回啦一个这一的普通对象 concilechildren时候这个对象成啦一个fiber

 {
    // This tag allow us to uniquely identify this as a React Portal
    $$typeof: REACT_PORTAL_TYPE,
    key: key == null ? null : '' + key,
    children,
    containerInfo,
    implementation,
  };
复制代码

下一次performunitofwork时候就是这个fiber tag是4一直下去建立fiber树

但是最终的移动元素是在commit阶段

2.forwardRef更新

形成的react内部组件的type是如下对象

return {
    $$typeof: REACT_FORWARD_REF_TYPE, //!给一个特定的ref_type
    render,//! render就是传进来的函数组件 
  };
复制代码

父元素的fiber的属性整体还是 只有protal是typeof会变化的然后没有type的其他都是typeof会变化的然后没有type的 其他都是typeof: react_element,然后靠type的$$type去区分

{
	pendingprops:{
		children:{
			$$typeof: react_element,
			type:{
				$$typeof: REACT_FORWARD_REF_TYPE,
				render
			}
		}
	}
}
复制代码

更新 forwardRef的创建fiber的第一次的children是来自render的调用返回的children 和函数组件一样

function updateForwardRef(
  current: Fiber | null,
  workInProgress: Fiber,
  type: any,
  nextProps: any,
  renderExpirationTime: ExpirationTime,
) {
  const render = type.render;
  const ref = workInProgress.ref;//!拿到传到forward组件上的进来的ref 自动调用react.createElement 但是ref不会和prop合并 所以直接在fiber上
  if (hasLegacyContextChanged()) {//!context相关
    // Normally we can bail out on props equality but if context has changed
    // we don't do the bailout and we have to reuse existing props instead.
  } else if (workInProgress.memoizedProps === nextProps) {
    const currentRef = current !== null ? current.ref : null;
    if (ref === currentRef) {//!ref没变直接不更新 ForwardRef更新只能是ref改变 
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  }
  //!props发生改变 函数组件的children需要reconcile
  let nextChildren;
  if (__DEV__) {
    ReactCurrentOwner.current = workInProgress;
    ReactCurrentFiber.setCurrentPhase('render');
    nextChildren = render(nextProps, ref);//!拿到ref之后作为参数传给啦函数组件 此时这个函数组件就是个普通函数 不会有单独的fiber 其返回值直接作为forwrardref的children
    ReactCurrentFiber.setCurrentPhase(null);
  } else {
    nextChildren = render(nextProps, ref);
  }

  reconcileChildren(
    current,
    workInProgress,
    nextChildren,
    renderExpirationTime,
  );
  return workInProgress.child;
}
复制代码

3.mode组件更新

调和到这个mode组件时候,mode组件的fiber对象上有个mode属性为你这个mode 比如是concurrentMODE 是二进制形式 调和孩子组件时候所有孩子fiber也会继续使用这个父组件的mode值 以此记录这个子树是在那个mode下,子组件的创建更新的expirationtime会有一个特殊的计算过程

4.memo组件更新

api

export default function memo<Props>(
  type: React$ElementType,
  compare?: (oldProps: Props, newProps: Props) => boolean,
) {
  if (__DEV__) {
    if (!isValidElementType(type)) {
      warningWithoutStack(
        false,
        'memo: The first argument must be a component. Instead ' +
          'received: %s',
        type === null ? 'null' : typeof type,
      );
    }
  }
  return {
    $$typeof: REACT_MEMO_TYPE,
    type,
    compare: compare === undefined ? null : compare,
  };
}
复制代码

返回对象为 作为type

{
    $$typeof: REACT_MEMO_TYPE,
    type,//!这个type是我们传进来的functionComponent的type
    compare: compare === undefined ? null : compare,
  };
复制代码

源码:

function updateMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,//!就是传到memo上的props 因为react.createElement会自动设置props
  updateExpirationTime,
  renderExpirationTime: ExpirationTime,
): null | Fiber {
  if (current === null) {//!current === null 主要是辨别是否是第一次渲染
    let type = Component.type;//!第一次渲染 
    // !Component是对象{
    //   $$typeof: REACT_MEMO_TYPE,
    //   type,//!这个type是我们传进来的函数组件
    //   compare: compare === undefined ? null : compare,
    // };
    //!那么获取的type就是函数组件(调用memo传进来的第一个参数)的type
    if (isSimpleFunctionComponent(type) && Component.compare === null) {//!看看我们传进来的第二个参数 即什么条件不刷新组件 如果没传入
      //!isSimpleFunctionComponent 表示是个纯函数组件不是类组件
      // If this is a plain function component without default props,
      // and with only the default shallow comparison, we upgrade it
      // to a SimpleMemoComponent to allow fast path updates.
      workInProgress.tag = SimpleMemoComponent;
      workInProgress.type = type;
      return updateSimpleMemoComponent(//!采用简单的判断条件来刷新组件
        current,
        workInProgress,
        type,//!函数组件
        nextProps,
        updateExpirationTime,
        renderExpirationTime,
      );
    }
    //!还是第一次创建 这里直接创建子函数组件的fiber作为memo的孩子(注意不是函数组件的children的fiber作为子child)
    let child = createFiberFromTypeAndProps(
      Component.type,
      null,
      nextProps,
      null,
      workInProgress.mode,
      renderExpirationTime,
    );
    child.ref = workInProgress.ref;
    child.return = workInProgress;
    workInProgress.child = child;
    return child;
  }
  //!不是第一次渲染 currentChild为上次的子函数fiber
  let currentChild = ((current.child: any): Fiber); // This is always exactly one child
  if (
    updateExpirationTime === NoWork ||
    updateExpirationTime > renderExpirationTime//!不需要更新
  ) {
    // This will be the props with resolved defaultProps,
    // unlike current.memoizedProps which will be the unresolved ones.
    const prevProps = currentChild.memoizedProps;
    // Default to shallow comparison
    let compare = Component.compare;
    compare = compare !== null ? compare : shallowEqual;
    //!判断函数组件的props和这次传给memo的props是否发生变化
    if (compare(prevProps, nextProps) && current.ref === workInProgress.ref) {//!传入啦compare 返回true则不需要更新
      return bailoutOnAlreadyFinishedWork(//!直接跳过
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  }
  //!需要更新 创建新的函数组件的fiber当作child
  let newChild = createWorkInProgress(
    currentChild,//!函数组件
    nextProps,//!新的props
    renderExpirationTime,
  );
  newChild.ref = workInProgress.ref;
  newChild.return = workInProgress;
  workInProgress.child = newChild;
  return newChild;
}

function updateSimpleMemoComponent(
  current: Fiber | null,
  workInProgress: Fiber,
  Component: any,
  nextProps: any,
  updateExpirationTime,
  renderExpirationTime: ExpirationTime,
): null | Fiber {
  if (
    current !== null &&
    (updateExpirationTime === NoWork ||
      updateExpirationTime > renderExpirationTime)//!当前组件需要更新
  ) {
    const prevProps = current.memoizedProps;
    if (
      shallowEqual(prevProps, nextProps) &&//!浅比较props
      current.ref === workInProgress.ref
    ) {
      //!不更新
      return bailoutOnAlreadyFinishedWork(
        current,
        workInProgress,
        renderExpirationTime,
      );
    }
  }
   //!把这个组件当作函数组件更新  Component就是那个函数 children通过调用这个函数获取
  return updateFunctionComponent(
    current,
    workInProgress,
    Component,
    nextProps,
    renderExpirationTime,
  );
}
复制代码

1.非简单更新

memo创建就是直接创建内部的函数组件fiber作为child(传给他传给他props) 只是多加啦一层判断是否要更新

memo更新就是走createWorkInProgress 复用之前的fiber改变下props 到那个函数组件调和时候再去recocile他的children

2.简单更新即updateSimpleMemoComponent

这个时候memo的child就是函数组件的children的第一个组件fiber 函数组件fiber就没了 以后更新也是对这个fiber进行更新 这个时候后面走的都是

updateSimpleMemoComponent这个方法(直接去更新函数组件即子组件) 而不是去走createWorkInProgress这个方法

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享