React16源码: React中的PortalComponent创建, 调和, 更新的源码实现

这篇具有很好参考价值的文章主要介绍了React16源码: React中的PortalComponent创建, 调和, 更新的源码实现。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

PortalComponent


1 )概述

  • React Portal之所以叫Portal,因为做的就是和“传送门”一样的事情
  • render到一个组件里面去,实际改变的是网页上另一处的DOM结构
  • 主要关注 portal的创建, 调和, 更新过程

2 )源码

定位到 packages/react-dom/src/client/ReactDOM.js#L576

function createPortal(
  children: ReactNodeList,
  container: DOMContainer,
  key: ?string = null,
) {
  invariant(
    isValidContainer(container),
    'Target container is not a DOM element.',
  );
  // TODO: pass ReactDOM portal implementation as third argument
  return ReactPortal.createPortal(children, container, null, key);
}

  • 这里调用的是 ReactPortal.createPortal, 进入

    // packages/shared/ReactPortal.js#L14
    export function createPortal(
      children: ReactNodeList,
      containerInfo: any,
      // TODO: figure out the API for cross-renderer implementation.
      implementation: any,
      key: ?string = null,
    ): ReactPortal {
      return {
        // This tag allow us to uniquely identify this as a React Portal
        $$typeof: REACT_PORTAL_TYPE,
        key: key == null ? null : '' + key,
        children,
        containerInfo, // dom 挂载节点
        implementation,
      };
    }
    
    • 这里返回一个对象,类似于 ReactElement
    • 区别在于 $$typeofcontainerInfo 需要的挂载点
  • 对于 REACT_PORTAL_TYPE 类型的组件在 reconcile 时, 看下具体操作, 找到 reconcileSinglePortal

    // packages/react-reconciler/src/ReactChildFiber.js#L1171
    function reconcileSinglePortal(
      returnFiber: Fiber,
      currentFirstChild: Fiber | null,
      portal: ReactPortal,
      expirationTime: ExpirationTime,
    ): Fiber {
      const key = portal.key; // 当前的 key
      let child = currentFirstChild;
      // 如果 存在 child, 对比 child.key === key
      while (child !== null) {
        // TODO: If key === null and child.key === null, then this only applies to
        // the first item in the list.
        if (child.key === key) {
          // 关注这里,有 containerInfo 的对比,portal 需要关心渲染到的节点是否有变化
          // 如果节点有变化,那么这个 portal 的渲染过程也会有变化
          // 都符合,说明老节点都可以复用
          if (
            child.tag === HostPortal &&
            child.stateNode.containerInfo === portal.containerInfo &&
            child.stateNode.implementation === portal.implementation
          ) {
            deleteRemainingChildren(returnFiber, child.sibling); // 删除其他节点
            // 通过 useFiber 复用这个节点
            const existing = useFiber(
              child,
              portal.children || [],
              expirationTime,
            );
            existing.return = returnFiber;
            return existing;
          } else {
            // key相同,但是不符合上述if, 没法复用,删除干净
            deleteRemainingChildren(returnFiber, child);
            break;
          }
        } else {
          // key 不同,删除当前节点
          deleteChild(returnFiber, child);
        }
        child = child.sibling;
      }
    
      // 创建一个 portal 的 fiber 对象
      const created = createFiberFromPortal(
        portal,
        returnFiber.mode,
        expirationTime,
      );
      created.return = returnFiber;
      return created;
    }
    
  • 对于更新一个portal节点,进入 updatePortalComponent文章来源地址https://www.toymoban.com/news/detail-810808.html

    // packages/react-reconciler/src/ReactFiberBeginWork.js#L1322
    function updatePortalComponent(
      current: Fiber | null,
      workInProgress: Fiber,
      renderExpirationTime: ExpirationTime,
    ) {
      // 挂载点处理
      pushHostContainer(workInProgress, workInProgress.stateNode.containerInfo);
      const nextChildren = workInProgress.pendingProps; // 这里 pendingProps 是 children
      if (current === null) {
        // Portals are special because we don't append the children during mount
        // but at commit. Therefore we need to track insertions which the normal
        // flow doesn't do during mount. This doesn't happen at the root because
        // the root always starts with a "current" with a null child.
        // TODO: Consider unifying this with how the root works.
        workInProgress.child = reconcileChildFibers(
          workInProgress,
          null,
          nextChildren,
          renderExpirationTime,
        );
      } else {
        reconcileChildren(
          current,
          workInProgress,
          nextChildren,
          renderExpirationTime,
        );
      }
      return workInProgress.child;
    }
    
    • 之前梳理过的 API 不再赘述
    • 一些API的调用,在源码的注释中

到了这里,关于React16源码: React中的PortalComponent创建, 调和, 更新的源码实现的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • React16源码: React中的IndeterminateComponent的源码实现

    IndeterminateComponent 1 )概述 这是一个比较特殊的component的类型, 就是还没有被指定类型的component 在一个fibrer被创建的时候,它的tag可能会是 IndeterminateComponent 在 packages/react-reconciler/src/ReactFiber.js 中,有一个方法 createFiberFromTypeAndProps 中,一开始就声明了 在最终调用 createFibe

    2024年01月21日
    浏览(41)
  • React16源码: React中的updateHostRoot的源码实现

    HostRoot 的更新 1 )概述 HostRoot 是一个比较特殊的节点, 因为在一个react应用当中 它只会有一个 HostRoot , 它对应的 Fiber 对象是我们的 RootFiber 对象 重点在于它的更新过程 2 )源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js#L612 HostRoot 创建更新的过程就是在 ReactFiberReconcile

    2024年01月22日
    浏览(43)
  • React16源码: React中的unwindWork的源码实现

    unwindWork 1 )概述 在 renderRoot 的 throw Exception 里面, 对于被捕获到错误的组件进行了一些处理 并且向上去寻找能够处理这些异常的组件,比如说 class component 里面具有 getDerivedStateFromError 或者 componentDidCatch 这样的生命周期方法 这个class component 就代表它可以处理它的子树当中渲

    2024年01月25日
    浏览(37)
  • React16源码: React中的updateClassComponent的源码实现

    ClassComponent 的更新 1 ) 概述 在 react 中 class component,是一个非常重要的角色 它承担了 react 中 更新整个应用的API setState forceUpdate 在react当中,只有更新了state之后,整个应用才会重新进行渲染 在 class component 中, 它的逻辑相对复杂 2 )源码 在 packages/react-reconciler/src/ReactFiberB

    2024年01月21日
    浏览(40)
  • React16源码: React中的completeUnitOfWork的源码实现

    completeUnitOfWork 1 )概述 各种不同类型组件的一个更新过程对应的是在执行 performUnitOfWork 里面的 beginWork 阶段 它是去向下遍历一棵 fiber 树的一侧的子节点,然后遍历到叶子节点为止,以及 return 自己 child 的这种方式 在 performUnitOfWork 里面,还有一个方法叫做 completeUnitOfWork 在

    2024年01月23日
    浏览(44)
  • React16源码: React中的renderRoot的源码实现

    renderRoot 1 )概述 renderRoot 是一个非常复杂的方法 这个方法里处理很多各种各样的逻辑, 它主要的工作内容是什么? A. 它调用 workLoop 进行循环单元更新 遍历整个 Fiber Tree,把每一个组件或者 dom 节点对应的 Fiber 节点拿出来单一的进行更新,这是一个循环的操作 把整棵 Fiber T

    2024年01月17日
    浏览(43)
  • React16源码: React中的setState和forceUpdate源码实现

    setState 和 forceUpdate 1 ) 概述 通过 class component 内部的 setState ,以及 forceUpdate 去更新一个组件的过程 在react的应用当中,我们只有 ReactDOM.render setState ,以及 forceUpdate 这几种种方式去更新react的应用是合理的,其他没有什么特别常用的方式去更新了 而且react官方推荐的也是用

    2024年01月25日
    浏览(40)
  • React16源码: React中的HostComponent & HostText的源码实现

    HostComponent HostText 1 )概述 HostComponent 就是我们dom原生的这些节点, 如: div, span, p 标签这种 使用的是小写字母开头的这些节点一般都认为它是一个 HostComponent HostText ,它是单纯的文本节点 主要关注它们的一个更新过程 2 )源码 定位到 packages/react-reconciler/src/ReactFiberBeginWork.js 进

    2024年01月24日
    浏览(41)
  • React16源码: React中的不同的expirationTime的源码实现

    不同的 expirationTime 1 )概述 在React中不仅仅有异步任务 大部分情况下都是同步的任务,所以会有不同 expirationTime 的存在 2 )种类 A. Sync 模式,优先级最高 任务创建完成之后,立马更新到真正的dom里面 是一个创建即更新的流程 B. Async 模式, 异步模式 会有一个调度 包含一系列

    2024年02月01日
    浏览(38)
  • React16源码: React中的update和updateQueue的源码实现

    React中的update和updateQueue 1 )概述 在 ReactDOM.render 过程中,还需要创建一个 update 对象 update 用于记录组件状态的改变的一个对象,它存放于Fiber对象的 updateQueue 中 updateQueue ,它是一个单向链表的结构,一次整体的更新过程当中 可能在这个queue里会存在多 Update 在这次更新的过

    2024年02月02日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包