源码分析之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.tag为ForwardRef(11)。

beginWork阶段

若fiber.tag为ForwardRef时,则调用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一起使用,用于子组件向父组件提供方法或变量等。

相关推荐
deli0074 分钟前
DLA 枝晶生长实测:5000 个粒子集体停摆之后,分形维数收敛到 1.70
前端
IT_陈寒13 分钟前
Redis卡顿的锅,这次真不是大key的错
前端·人工智能·后端
可乐鸡翅yeah_20 分钟前
video.js 集成 hls.js 开发 M3U8 播放器,新手高频踩坑
开发语言·前端·javascript·后端·ecmascript·m3u8·音视频在线播放
程序猿追1 小时前
HarmonyOS 6 上做个极简浏览器:Web 组件 + 前进后退 + 加载进度
前端·华为·harmonyos
沐沐师1 小时前
Mybatis 框架教程
前端
三天不学习1 小时前
Tailwind CSS 快速入门(2026 版):从 v4 零配置上手,到「该不该用、怎么用好」的选型实战
前端·css·tailwind
明月_清风1 小时前
面对陌生的 GitHub 项目无从下手?这 4 个网站帮你快速读懂源码
前端·后端·github
htzyl2062 小时前
前端阶梯——第九章、颜色、文本与背景
前端·css
zhangzeyuaaa2 小时前
Ruby `require` 完全指南:从 `$LOAD_PATH` 到 `require_relative`
服务器·前端·ruby
htzyl2062 小时前
前端阶梯——第八章、CSS入门与选择器
前端·css