源码分析之React中的forwardRef解读

概述

React.forwardRef用于包裹函数组件,向其传递第二个参数ref。普通函数组件只接受一个参数props

源码分析

forwardRef接受一个函数参数render,render可以是函数组件,返回一个ReactNode

其源码如下,返回一个对象

js 复制代码
function forwardRef(render){
  return {
    $$typeof:Symbol.for("react.forward_ref"),// 标识是forwardRef类型
    render,
  }
}

在生成fiber时,其fiber.tagForwardRef(11)。

beginWork阶段

fiber.tagForwardRef时,则调用updateForwardRef方法,用于处理forwardRef类型的fiber子节点。

  • updateForwardRef方法

updateForwardRef方法要做的事就是拆解,将fiber.ref作为渲染函数render的第二个参数。

js 复制代码
function updateForwardRef(current,workInProgress,Component,nextProps,renderLanes){
 // 读取渲染函数
 const render= Component.render;
 // 读取 fiber.ref 它本质是就是一个ref对象 即{current:null}
 const ref = workInProgress.ref;
 
 // 过滤掉nextProps上的ref属性,作为render的第一个参数
  let propsWithoutRef;
  if ('ref' in nextProps) {
    propsWithoutRef = {};
    for (const key in nextProps) {
      if (key !== 'ref') {
        propsWithoutRef[key] = nextProps[key];
      }
    }
  } else {
    propsWithoutRef = nextProps;
  }
  // 其余函数组件的差不多
  
  // 准备好读取context的上下文
  prepareToReadContext(workInProgress, renderLanes);
  const nextChildren = renderWithHooks(
    current,
    workInProgress,
    render,
    propsWithoutRef,
    ref,// 注意这个参数
    renderLanes,
  );

  // bailout策略,组件更新时,props无变化会触发
  if (current !== null && !didReceiveUpdate) {
    bailoutHooks(current, workInProgress, renderLanes);
    return bailoutOnAlreadyFinishedWork(current, workInProgress, renderLanes);
  }

  // 遍历子树
  reconcileChildren(current, workInProgress, nextChildren, renderLanes);
  
  // 返回子节点
  return workInProgress.child;
}
  • renderWithHooks方法

renderWithHooks方法顾名思义就是执行渲染函数包括函数组件内部的hooks

其源码如下:

js 复制代码
function renderWithHooks<Props, SecondArg>(
  current,
  workInProgress,
  Component,
  props,
  secondArg,
  nextRenderLanes,
) {
  // 渲染优先级赋值给renderLanes,下同currentlyRenderingFiber,在某些hooks调用时会用到
  renderLanes = nextRenderLanes;
  // 正在构建的fiber复制给currentlyRenderingFiber,方便在hooks中调用
  currentlyRenderingFiber = workInProgress;
  
  // 将fiber上的memoizedState、updateQueue以及渲染优先级清理
  workInProgress.memoizedState = null;
  workInProgress.updateQueue = null;
  workInProgress.lanes = NoLanes;

  // 切换hooks 分发器
  ReactSharedInternals.H =
      current === null || current.memoizedState === null
        ? HooksDispatcherOnMount
        : HooksDispatcherOnUpdate;
  // 执行组件,即forwardRef中的render,secondArg就是ref
  let children = Component(props, secondArg);
  
  // 若是在渲染阶段又触发了state变化
  if (didScheduleRenderPhaseUpdateDuringThisPass) {
    children = renderWithHooksAgain(
      workInProgress,
      Component,
      props,
      secondArg,
    );
  }
  
  // 主要就是清理工作
  finishRenderingHooks(current, workInProgress, Component);

  return children;
}

总结

React.forwardRef就是给函数包装了一层,多传了一个ref参数,其余和普通函数组件一样,通常情况下和useImperativeHandle hook一起使用,用于子组件向父组件提供方法或变量等。

相关推荐
宿674几秒前
vue3-config
前端·javascript·vue.js
明月_清风1 分钟前
位图与布隆过滤器:海量数据下的"存在性判断"艺术
前端·后端·算法
明月_清风9 分钟前
Hash 表从入门到精通:Go 实战与工程细节
前端·后端·算法
非凡ghost2 小时前
AI修图不“造假“,摄影师的出片效率加速器
服务器·前端·人工智能·电脑
2601_951615202 小时前
网页设计模板源码 web前端模板网页源码下载
前端
mmsx2 小时前
osmdroid 地图实战 03|地图的"分层世界":离线方案、图层模型与 Overlay 体系
android·前端
郭邯2 小时前
从零到一:我用 AI 写了个复利计算器,顺便治好了我的"公式恐惧症"
前端
Pointer Pursuit2 小时前
C++11特性(二)
前端·c++·算法
天道kabuto2 小时前
Vue3 升级踩坑:子组件 click 事件为什么会触发两次?
前端
Nayana2 小时前
《Web 到 HarmonyOS》-- Ability、Stage、模块化与路由导航
前端