React16源码: createRef与forwardRef源码实现

React 源碼解析 - API 系列 - React.createRef/forwardRef React 源碼解析 - API 系列 - React.createRef/forwardRef前言正文關於 refref 的由來string refcallback refcreateRefuseRefforwardRefuseImperativeHandleReact.createRef源碼分析RefObjectReact.forwardRef源碼分析結語 前言 這篇一樣繼續來分析 react 中的 createRef() 以及 forwardRef() 這兩個 api,首先也會先說說 ref 的概念以 阅读详情

ref 概述

  • 拿一个场景举例开始
    • 比如,在react当中写一个组件,类型是 class Component
    • 在 render function 里面渲染一系列的子组件或者 dom节点
    • 会有这样的需求,就是获取某个dom节点或某个子组件的实例来做一些手动的操作
    • 不局限于 props 更新这种方式来更新这个节点,获取节点后,可以绑定某些事件做一些操作
    • 如果没有其他好方法,可能要自己去写 document.querySelector之类的选择器来获取这个 dom 节点
  • 在 react 当中就为我们提供了这种方式
    • 通过 ref 这个特殊的 attribute 可以非常方便的在一个组件内部
    • 获取到它的一个子节点的实例对象, 这就是 ref 的一个核心功能
  • 在 react 当中有三种使用ref的方式
    • string ref
    • function
    • createRef

ref 应用示例

1 ) 演示代码

import React from 'react'

export default class RefDemo extends React.Component {
  constructor() {
    super()
    this.objRef = React.createRef()
  }

  componentDidMount() {
    setTimeout(() => {
      this.refs.stringRef.textContent = 'string ref'
      this.methodRef.textContent = 'method ref'
      this.objRef.current.textContent = 'obj ref'
    }, 1000)
  }

  render() {
    return (
      <>
        <p ref="stringRef">xxxx</p>
        <p ref={ele => (this.methodRef = ele)}>yyyy</p>
        <p ref={this.objRef}>zzzz</p>
      </>
    )
  }
}

2 )代码说明

  • 第一种使用了 stringRef
    • 也就是我们在想要获取的那个节点的 props上面
    • 使用了一个 ref 属性, 然后传入一个字符串, react在完成这一个节点的渲染之后
    • 它会在 this.refs 这个对象上面挂载这个string对应的一个key
    • 这个key所指向的就是我们这个节点实例的对象
    • 如果是dom节点, 它就会对应dom的实例
    • 如果是子组件,就是子组件的实例,即:class Component
    • 如果是一个 function Component,正常来讲它是会失败的
    • 拿到的会是一个 undefined,因为 function Component 没有实例
    • 如果不让其出错,需要使用 forwardRef,后续来说
      * 此种方式不被推荐
  • 第二种使用了 function
    • 在 ref 属性上传入一个 method 方法,它接受一个参数
    • 这个参数就是一个节点对应的实例
      • 如果是dom节点对应的是dom实例
      • 如果是组件对应的是组件的实例
    • 然后在this上面去挂载某一个属性,比如上面的 methodRef, 基于其 textContent 属性来重新赋值
  • 第三种是通过 React.createRef 这个API
    • 在 class Component 内部,我们使用 this.objRef = React.createRef() 去创建了一个对象
      • 这个对象相当于 { current: null} , 默认值是 null
    • 把这个对象传给某一个节点的 ref 属性上,在组件渲染完成之后
    • 会把这个节点对应的实例挂载到这个对象的 current 这个属性上面
    • 调用它就是通过 this.objRef.current 操作它就可以了
  • 这个 demo 效果是不用跑也知道是怎样的一个变化
  • 以上三种情况的前两种没有在源码中有过多的体现,我们看下第三种情况 createRef

3 )源码探究

  • 从入口文件 React.js 中可见

    import {createRef} from './ReactCreateRef';
    
  • 定位到 ReactCreateRef.js 中

    /**
     * Copyright (c) Facebook, Inc. and its affiliates.
     *
     * This source code is licensed under the MIT license found in the
     * LICENSE file in the root directory of this source tree.
     * @flow
     */
    
    import type {RefObject} from 'shared/ReactTypes';
    
    // an immutable object with a single mutable value
    export function createRef(): RefObject {
      const refObject = {
        current: null,
      };
      if (__DEV__) {
        Object.seal(refObject);
      }
      return refObject;
    }
    
  • 这个代码看上去,非常简单,它返回的就是一个简单的对象

  • DEV 相关的判断不是核心,忽略即可

  • 它里面的属性 current, 默认值是 null

forward ref

1 )示例演示

  • 关于 forward ref, 先看一个示例

    import React from 'react'
    
    const TargetComponent = React.forwardRef((props, ref) => (
      <input type="text" ref={ref} />
    ))
    
    export default class Comp extends React.Component {
      constructor() {
        super()
        this.ref = React.createRef()
      }
    
      componentDidMount() {
        this.ref.current.value = 'ref input'
      }
    
      render() {
        return <TargetComponent ref={this.ref} />
      }
    }
    
    • 在这个文件下,定义了两个组件 Comp, TargetComponent
    • Comp 组件
      • 里面创建了一个ref, 并传递给 TargetComponent 组件
      • ref 是为了去获取某一个节点的实例的,通常会获取dom节点的实例
      • 有时,也会获取一下class Component 的实例
      • 如果组件是一个 纯的 function Component, 没有实例,则会出错
        • 如果我是一个组件的提供者,就是开源了一些组件
        • 用户(开发者) 一开始并不知道是否是有实例的组件
        • 还有就是,比如一些包, 如 Redux,提供的 connect 方法里的 HOC (高阶组件)
        • 作为用户想要获取自己写的组件的实例的时候,传入ref,获得的是被包装过的组件
      • 解决之道是外面套上 React.forwardRef,如上代码所示
        • forwardRef 可以帮助我们实现一个 ref 的传递
    • TargetComponent 组件
      • 就是我们基于 React.forwardRef 实现的 function Component
      • 如果要把它改造成HOC, 也可以基于此函数的回调将 ref 进行传递挂载
      • 这样就不会违背开发者的意图

2 )源码分析

定位到 forwardRef.js 中

/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

import {REACT_FORWARD_REF_TYPE} from 'shared/ReactSymbols';

import warningWithoutStack from 'shared/warningWithoutStack';

export default function forwardRef<Props, ElementType: React$ElementType>(
  render: (props: Props, ref: React$Ref<ElementType>) => React$Node,
) {
  if (__DEV__) {
    if (typeof render !== 'function') {
      warningWithoutStack(
        false,
        'forwardRef requires a render function but was given %s.',
        render === null ? 'null' : typeof render,
      );
    } else {
      warningWithoutStack(
        // Do not warn for 0 arguments because it could be due to usage of the 'arguments' object
        render.length === 0 || render.length === 2,
        'forwardRef render functions accept exactly two parameters: props and ref. %s',
        render.length === 1
          ? 'Did you forget to use the ref parameter?'
          : 'Any additional parameter will be undefined.',
      );
    }

    if (render != null) {
      warningWithoutStack(
        render.defaultProps == null && render.propTypes == null,
        'forwardRef render functions do not support propTypes or defaultProps. ' +
          'Did you accidentally pass a React component?',
      );
    }
  }

  return {
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };
}
  • 忽略 DEV 判断代码,其本质上就只有一个 return 对象

    return {
      $$typeof: REACT_FORWARD_REF_TYPE,
      render,
    };
    
    • $$typeoff,是 REACT_FORWARD_REF_TYPE
      • 注意
        • 使用 forwordRef 时, 用这个API最终创建出来的 ReactElement
        • 比如上面示例的 TargetComponent组件最终会被创建成一个 ReactElement,比如,别名叫 A
        • TargetComponent组件 就是 forwordRef 返回的对象,这个不难理解,因为代码如此
        • TargetComponent 对应的 组件A 的 type 是 TargetComponent
        • TargetComponent 对应的 组件A的 $$typeof 是 REACT_ELEMENT_TYPE
          • A的 $$typeof 不是 REACT_FORWARD_REF_TYPE
          • 因为,所有通过React.createElement创建的节点的 $$typeof 都是 REACT_ELEMENT_TYPE
          • TargetComponent组件最终会被React.createElement创建出来
          • 这一点不要搞混了
    • render
      • 就是我们传进来的 function component
在React 中,如何创建refs? 无论使用哪种方式,创建的 ref 对象都可以通过 .current 属性来访问引用的组件或 DOM 元素。通常,在组件的构造函数中将 ref 赋值给类的实例属性。另一种方式是使用回调函数形式的 ref,通过在组件中定义一个函数,然后将其作为 ref 属性传递给组件或 DOM 元素。在函数组件中,可以使用 React.useRef() Hook 来创建一个 ref 对象,并将其赋值给一个变量。当组件渲染时,React 会自动调用该回调函数,并将组件或 DOM 元素的引用作为参数传递给它。 阅读详情

相关推荐

做小程序支付时,我是怎么被“虚拟支付”绊了一跤的

微信小程序支付开发中,普通支付与虚拟支付存在本质差异。开发者常误将虚拟权益(会员、点数等)当作普通商品支付,导致审核失败。两者在审核关注点、结算方式、API调用及平台政策上都截然不同。关键点在于:1)先明确交易对象类型;2)产品命名需真实反映权益性质;3)支付成功≠业务完成;4)审核说明要详细描述交付逻辑;5)避免依赖过时规则。正确顺序应是:判断商品属性→选择支付类型→设计订单系统→准备审核材料。这个认知差异比技术实现更重要,提前理解平台规则可大幅减少返工。

zhurizhe71的学习之路 2588

React中Ref使用详解(createRef、回调ref、useRef、forwardRef、useImperativeHandle)

目录 类组件中使用 ref='myRef'(React中不推荐) createRef(推荐) 回调Ref (推荐) 函数组件中使用 React.useRef React.forwardRef React.useImperativeHandle 由于函数组件中没有实例,所以区分2种用法。 类组件中使用 注意你不能在函数组件上使用下面的ref属性,因为他们没有实例。 ref='myRef'(React中不推荐) 和vue中的ref方法使用相同,在元素上添加ref=‘myRef’,.

AIWWY的博客 1万+

宜搭子表单(自定义页面全部展示)

效果:增加自定义页面添加表格组件,配置数据源变量: formUuid'表单编码'变量: pageSize10变量: currentPage1。

2301_80286061的博客 2283

React学习笔记(三):引用(Refs)& DOM

概述:    引用提供了一个获得DOM节点或者创建在render方法中的React元素的方法;    在典型的React数据流中,props是唯一的父组件与它们的子元素的通信方式。更改子元素,你需要使用新的props去重新渲染子元素。但是在一些情况下你现在典型数据流之外强制的更改元素。被更改的子元素可能是一个React组件的实例,或者是一个DOM元素。对所有这些情况,React提供了一种特殊方法:...

一个大神的发展史 7330

博图V16与840D sl数控系统PLC编程实战指南

本文为西门子840D sl数控系统的PLC编程实战指南,详细介绍了如何使用博图V16软件配置编程环境。内容涵盖从项目创建、核心程序块(如DB、FB)解析到OB1主循环编程的完整流程,并提供了高级调试技巧与联调避坑指南,帮助工程师快速上手并高效完成系统集成与逻辑控制。

weixin_29193259的博客 528

pytorch实现手语识别

连续手语识别项目,中文数据集CSL

masteryi-0018的博客 1万+

useRef和createRef

在react中,我们可以使用ref来获取组件示例或者DOM元素。 一般使用方法: const ref = useRef(initialValue); <input ref={ref} /> 注意: useRef返回一个可变的 ref 对象,该对象只有个 current 属性,初始值为传入的参数( initialValue ); 返回的 ref 对象在组件的整个生命周期内保持不变,除非手动修改; 当更新 current 值时并不会 re-render ,这是与 useState 不同的地方;

weixin_43636454的博客 2003

嵌入式状态机编程-QP状态机框架与常见状态机方法

状态机基本术语 现态:是指当前所处的状态。 条件:又称为“事件”,当一个条件被满足,将会触发一个动作,或者执行一次状态的迁移。 动作:条件满足后执行的动作。动作执行完毕后,可以迁移到新的状态,也可以仍旧保持原状态。动作不是必需的,当条件满足后,也可以不执行任何动作,直接迁移到新状态。 次态:条件满足后要迁往的新状态。“次态”是相对于“现态”而言的,“次态”一旦被激活,就转变成新的“现态”了。 传统有限状态机Fsm实现方法 如图,是一个定时计数器,计数器存在两种状态,一种为设置状态,一种为计时状态 设

qq_36969440的博客 9666

24 React.createRef()用法细节分析

通过React.createRef → ref对象 通过元素的ref属性可以附加到React元素上 一般通过构造器中给this的属性赋值一个ref,方便整个组件使用 ref只要传递到react元素中,就可以利用ref的current属性访问到该真实DOM节点 ref在componentDidMount和componentDidUpdate触发前更新 current里是null的现象 由于打印时,this.modalRef才刚刚声明,current是null,但是current是个引用值,展开时访问的是.

Stephy_Yy 2174

《手把手教你修复过期K8s集群证书:记一次K8s集群起死回生实录》

今天本想打开半年前部署的K8s测试集群跑个Demo,结果`kubectl`直接报错`certificate has expired`!作为一名云原生老司机,我决定把这次'抢救'过程记录下来,手把手带大家解决这个运维常见问题。

✨ 欢迎来到【Seal ^_^ 的优快云博客】!✨ 3781

硬件工程师提前批真题及答案解析2

我们需要考虑题目可能的简化假设,即输入电阻 Ri​ 可能是 rbe​ 和 βre​ 的组合,但题目可能简化为: Ri​≈rbe​+βre​。由于锗管的正向导通电压较小(0.2 V~0.3 V),而硅管的正向导通电压较大(0.6 V~0.7 V),因此-4.7 V的电压差不符合硅管的特性,而更接近锗管的特性。在三极管电路中,静态电位是指在无信号输入时,三极管各电极的直流电压。re​ 是动态发射极电阻,通常可以近似为 re​=IE​VT​​,但在题目中已经给出了 re​=300Ω,因此可以直接使用这个值。

XU157303764的博客 783

第一部分--基础--React API

React 虽然平时我们都喜欢说我们用React作为我们的核心框架,但其实大部分人都不知道React到底是个什么东东。事实上自从Facebook把React和ReactDOM分包发布之后,React就不仅仅是一开始的前端框架了,如果在15版本之后去看一下react和react-dom的源码大小,你就会发现,react仅仅1000多行代码,而react-dom却将近2w行。是的你没看错,而且你很可...

weixin_42854323的博客 269

AMBA-CHI协议详解(一)

CHI体系结构是一种可扩展、一致性的集线器接口和片上互联,可由多个组件使用。CHI体系结构允许根据性能、功耗和面积系统要求灵活地进行组件连接拓扑。硬件一致性使系统组件能够共享内存,而无需维护软件缓存以保持一致性。如果两个组件对同一存储器位置的写入可被所有组件以相同的顺序观察到,则存储器区域是一致的。

Crazzy_M的博客 1万+

ACE-Step UI 本地部署教程:把自有 GPU 变成免费 AI 音乐工作室

还在为每首 AI 歌曲付订阅费、受生成次数限制吗?ACE-Step UI 是一款免费开源的 AI 音乐生成工具,它在你本地电脑上运行开源的 ACE-Step 1.5 模型,输入歌词和风格描述,就能无限次生成带人声的完整歌曲——数据全程不出设备,作品完全归你所有。 [![ACE-Step UI 主界面演示:本地 AI 音乐生成创作流程](https://raw.gitcode.com/GitHub

gitblog_00307的博客 696

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

React ref 深度解析:回调 ref 重复执行与 createRef 的边界问题

在 React 组件开发中,操作 DOM 节点离不开 ref 机制。从早期的字符串 ref 到 createRef、回调 ref,再到函数组件中的 useRef,React 一直在将 ref 从隐式黑盒改造为显式、可控且可预测的能力。理解 ref 的演进原理,能帮助开发者规避许多隐蔽的运行时问题。例如,内联回调 ref 在组件更新时会被刻意执行两次——先传 null 清理旧引用,再传真实节点绑定新引用,这是协调机制对引用变更的标准处理;而 createRef 每次渲染都会返回全新对象,若误用于函数组件,则可

weixin_30972263的博客 212

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

React ref 的前世今生

作者:淡苍众所周知,React 通过声明式的渲染机制把复杂的 DOM 操作抽象成为简单的 state 与 props 操作,一时圈粉无数,一夜间将前端工程师从面条式的 D...

全栈前端精选 152

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

基于React版本16.4的源码解析(一)

本次分析的源码采用的是16.4.1的版本 核心接口 React.Children 提供了处理 this.props.children 的工具集: map forEach count toArray only 我们先来看看mapChildren做了什么事情 map: mapChildren 即当children为空时,返回nul...

weixin_34274029的博客 430

React 19 Migration Audit Report

Generated: [ISO timestamp] React current version: [version] ## Executive Summary - 🔴 Critical (breaking): [N] - 🟡 Deprecated (should migrate): [N] - 🔵 Test-specific: [N] - ℹ️ Informational: [N] -

gitblog_00722的博客 1107

React中的 ref 及原理浅析

我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。 前言 对于 ref 的理解,我们一部人还停留在用 ref 获取真实 dom 元素和获取组件层面上,但实际 ref 除了这两项功能之外,在使用上还有很多小技巧。本章我们就

fenfenfen520a的博客 134

GetQzonehistory 快速上手:把 QQ 空间历史说说导出成本地 Excel

QQ 空间首页翻不到几年前的动态,想给老账号做一次备份却找不到入口。GetQzonehistory 是一个跑在自己电脑上的 Python 脚本,扫码登录后批量拉取历史消息,最终把 QQ 空间历史说说导出成 Excel 表格和网页,所有文件只留在本机。 ## 它是什么 - 📁 运行位置:纯本地命令行程序,解析、分类、落盘全部发生在你的电脑上; - 登录方式:手机 QQ 扫码,全程不输密码; -

gitblog_00466的博客 218

基于 awesome-copilot 的 React 18 类组件迁移实战:react18-class-surgeon 的生命周期与 API 语义化改造指南

在 React 16/17 时代的类组件代码库里,`componentWillMount`、`componentWillReceiveProps`、`componentWillUpdate`、Legacy Context、字符串 ref 与 `findDOMNode` 等模式往往"静默工作了多年"。升级到 React 18.3.1 后,这些废弃 API 会被显式告警暴露出来——它们正是 React

gitblog_00582的博客 486

如何用 awesome-copilot 的 react19-upgrade 插件完成 React 18 到 19 的迁移与测试修复?

你的目标是把一个 React 18 代码库升级到 React 19,并且让构建和全部测试在升级后通过。awesome-copilot 仓库中的 `react19-upgrade` 插件提供了完成这件事的工具链:四个专职 agent(审计、依赖升级、源码迁移、测试修复)和三个模式参考 skill,由 `react19-commander` 按顺序调度,每个阶段有明确的准入门槛(gate),不达标不进

gitblog_00645的博客 371

WordPress.com Calypso 中 Paste-to-Link 高阶组件:粘贴即生成超链接的完整实现剖析

Paste-to-Link 是 WordPress.com 客户端(Calypso)[client/lib/paste-to-link](https://link.gitcode.com/i/b3de70d1b43e79f66626d351219f69fd) 目录下的一个高阶组件(HOC,Higher-Order Component),它赋予被包裹组件一种"智能粘贴"能力:当剪贴板中恰好是一个 U

gitblog_01182的博客 179
上一篇: React16源码: Component与PureComponent源码实现
下一篇: React16源码: context用法与createContext源码实现
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值