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

相关推荐
战族狼魂2 小时前
AI 全程聊天式交互,自动修复错误--撸了一个中英多语言电商独立站,基于SpringBoot+React+MySQL 可Docker一键部署
spring boot·mysql·react.js
mengsi552 小时前
Antigravity IDE 在浏览器上 verify 成功但本地 IDE 没反应 “开启Tun依然无济于事” —— 解决方案
前端·ide·chrome·antigravity
南风知我意9572 小时前
JavaScript 惰性函数深度解析:从原理到实践的极致性能优化
开发语言·javascript·性能优化
Можно2 小时前
pages.json 和 manifest.json 有什么作用?uni-app 核心配置文件详解
前端·小程序·uni-app
hzhsec2 小时前
钓鱼邮件分析与排查
服务器·前端·安全·web安全·钓鱼邮件
早點睡3902 小时前
ReactNative项目OpenHarmony三方库集成实战:react-native-render-html
react native·react.js·html
爱看老照片2 小时前
uniapp传递数值(数字)时需要使用v-bind的形式(加上冒号)
javascript·vue.js·uni-app
#做一个清醒的人2 小时前
Electron 保活方案:用子进程彻底解决原生插件崩溃问题
前端·electron·node.js