源码分析之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一起使用,用于子组件向父组件提供方法或变量等。

相关推荐
kyriewen1 分钟前
我用AI把公司10万行代码屎山重构了,CTO看了代码后说:你提前转正
前端·javascript·ai编程
ttwuai4 分钟前
XYGo Admin 菜单与路由:Vue3 动态路由 + GoFrame 权限菜单的完整实现方案
前端·vue·后台框架
程序员码歌12 分钟前
OpenSpec 到 Superpowers:AI 编码从说清到做对
android·前端·人工智能
爱编程的小新☆12 分钟前
LangGraph4j工作流框架
前端·数据库·ai·langchain·langgraph4j
@PHARAOH28 分钟前
HOW - 构建一个轻量前后端一体服务
前端·微服务·服务端
无限进步_39 分钟前
【C++】C++11的类功能增强与STL变化
java·前端·数据结构·c++·后端·算法
一只小小Java41 分钟前
Echarts单表多图实现
前端·javascript·echarts
跟着珅聪学java41 分钟前
Element UI 的 Tabs 标签页开发教程
javascript·vue.js·elementui
用户887665426631 小时前
React 多步骤表单工程化落地:从 Zod Schema、React Hook Form 到 Zustand 持久化
react.js
dunky1 小时前
Spring AI 深度解析:把 LLM 抽象成 Spring Bean 的底层逻辑
前端