React16源码: createRef与forwardRef源码实现

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' objectrender.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的 $$typeofREACT_ELEMENT_TYPE
          • A的 $$typeof 不是 REACT_FORWARD_REF_TYPE
          • 因为,所有通过React.createElement创建的节点的 $$typeof 都是 REACT_ELEMENT_TYPE
          • TargetComponent组件最终会被React.createElement创建出来
          • 这一点不要搞混了
    • render
      • 就是我们传进来的 function component

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/594905.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM:字节码

JVM&#xff1a;字节码 前言1. JVM概述 前言 1. JVM概述 1. JVM vs JDK vs JRE JVM Java 虚拟机&#xff08;JVM&#xff09;是运行 Java 字节码的虚拟机。JVM 有针对不同系统的特定实现&#xff08;Windows&#xff0c;Linux&#xff0c;macOS&#xff09;&#xff0c;目的是…

实现文本 内容展开 / 收起

<template><el-table :data"tableData" style"width: 100%" height"250"><el-table-columnfixedprop"date"label"日期"width"150"></el-table-column><el-table-columnprop"name…

正向代理和反向代理的区别

我们作为用户是操作客户端的&#xff0c;所以对于客户端的代理就是正向的。牢记&#xff0c;我们是正向的&#xff0c;我们是正向的&#xff0c;我们是正向的。 正向代理和反向代理都是网络中常见的代理服务类型&#xff0c;它们在网络通信中的角色和功能有所不同。 正向代理&…

阿里云PolarDB数据库不同配置租用价格表

阿里云数据库PolarDB租用价格表&#xff0c;云数据库PolarDB MySQL版2核4GB&#xff08;通用&#xff09;、2个节点、60 GB存储空间55元5天&#xff0c;云数据库 PolarDB 分布式版标准版2核16G&#xff08;通用&#xff09;57.6元3天&#xff0c;阿里云百科aliyunbaike.com分享…

【数据湖架构】数据湖101:概述

数据湖是非结构化和结构化数据池&#xff0c;按原样存储&#xff0c;没有特定的目的&#xff0c;可以建立在多种技术上&#xff0c;如Hadoop&#xff0c;NoSQL&#xff0c;Amazon Simple Storage Service&#xff0c;关系数据库或各种组合根据一份名为“什么是数据湖”的白皮书…

微软截图工具SnippingTool_6.1.7601免费版

SnippingTool是一款win7系统自带的一款非常实用型截图工具&#xff0c;操作简单&#xff0c;点击“新建"可一键截图&#xff0c;截图之后会弹出编辑器&#xff0c;可以进行一些简单的勾画编辑操作&#xff0c;您可以使用笔、荧光笔、电子邮件或保存等选项。如果您的系统丢…

界面控件DevExpress Blazor Grid v23.2 - 支持全新的单元格编辑模式

DevExpress Blazor UI组件使用了C#为Blazor Server和Blazor WebAssembly创建高影响力的用户体验&#xff0c;这个UI自建库提供了一套全面的原生Blazor UI组件&#xff08;包括Pivot Grid、调度程序、图表、数据编辑器和报表等&#xff09;。 在这篇文章中&#xff0c;我们将介…

Android 13 - Media框架(30)- MediaCodec(五)

前一节我们了解了input buffer写入的流程&#xff0c;知道了起播写前几笔数据时会先获取graphic buffer&#xff0c;这一节我们就一起来了解下dequeueBufferFromNativeWindow是如何工作的。 1、dequeueBufferFromNativeWindow ACodec::BufferInfo *ACodec::dequeueBufferFromN…

基于Matlab的车道线检测技术研究与实现

一、摘要 车道线检测是自动驾驶和智能交通系统的重要组成部分&#xff0c;它对于车辆的导航和控制具有重要的作用。本文主要研究了基于Matlab的车道线检测技术&#xff0c;包括图像预处理、边缘检测、霍夫变换等步骤&#xff0c;并实现了一个车道线检测系统。实验结果表明&…

死锁的处理策略“预防死锁”-第三十七天

目录 前言 破坏互斥条件 破坏不剥夺条件 破坏请求和保持条件 静态分配法 破坏循环等待条件 顺序资源分配法 本节思维导图 前言 死锁的产生必须满足四个必要条件&#xff0c;只要其中一个或几个条件不满足&#xff0c;死锁就不会发生 破坏互斥条件 互斥条件&#xff1a;…

Swift并发的结构化编程

并发&#xff08;concurrency&#xff09; 早期的计算机 CPU 都是单核的&#xff0c;操作系统为了达到同时完成多个任务的效果&#xff0c;会将 CPU 的执行时间分片&#xff0c;多个任务在同一个 CPU 核上按时间先后交替执行。由于 CPU 执行速度足够地快&#xff0c;给人的错觉…

京东商品SKU API:跨境电商平台提升用户活跃度的关键

一.京东获得JD商品sku信息 API的介绍 京东获得JD商品sku信息 API是京东平台提供的一套接口&#xff0c;允许第三方开发者获取京东商品的各种信息&#xff0c;包括商品的SKU信息。通过这个API&#xff0c;跨境电商平台可以获取到关于商品的详细数据&#xff0c;如商品名称、规格…

【python入门】day17:模块化编程、math库常见函数

什么叫模块 模块的导入 导入所有&#xff1a;import 模块名称 导入指定&#xff1a;from 模块名称 import 函数/变量/类 python的math库 什么是math库 Python的math库是Python的内建库之一&#xff0c;它提供了许多数学函数&#xff0c;包括三角函数、对数函数、幂函数等&a…

Scikit-Learn线性回归(四)

Scikit-Learn线性回归四:梯度下降 1、梯度下降1.1、梯度下降概述1.2、梯度下降及原理1.3、梯度下降的实现2、梯度下降法求解线性回归的最优解2.1、梯度下降法求解的原理2.2、梯度下降法求解线性回归的最优解2.3、梯度下降法求解线性回归案例(波士顿房价预测)3、Scikit-Learn…

Android:FragmentActivity

FragmentActivity是androidx中提供的所有用来支持Fragments的Activity的基类,通常我们新建工程时,MainActivity继承自AppCompatActivity,而AppCompatActivity继承自FragmentActivity。 public class AppCompatActivity extends FragmentActivity implements AppCompatCallb…

我的创作纪念日三年收获和感悟

机缘 我刚开始接触创作也是最近几年开始&#xff0c;当初就是希望自己的收获分享给大家&#xff0c;不仅使自己成长&#xff0c;也可以带着大家一起成长&#xff0c;独乐乐不如众乐乐&#xff0c;人都是自私的以前我都是看到好的知识文章都是自己藏起来&#xff0c;发现收获的…

flink on k8s几种创建方式

在此之前需要部署一下私人docker仓库&#xff0c;教程搭建 Docker 镜像仓库 注意&#xff1a;每台节点的daemon.json都需要配置"insecure-registries": ["http://主机IP:8080"] 并重启 一、session 模式 Session 模式是指在 Kubernetes 上启动一个共享的…

NSSCTF 简单包含

开启环境: 使用POST传flag&#xff0c;flag目录/var/www/html/flag.php 先使用post来尝试读取该flag.php 没反应: 查看一下源码index.php&#xff0c;看有什么条件 base64解密: <?php$path $_POST["flag"];if (strlen(file_get_contents(php://input)) <…

Qt/C++编写视频监控系统82-自定义音柱显示

一、前言 通过音柱控件实时展示当前播放的声音产生的振幅的大小&#xff0c;得益于音频播放组件内置了音频振幅的计算&#xff0c;可以动态开启和关闭&#xff0c;开启后会对发送过来的要播放的声音数据&#xff0c;进行运算得到当前这个音频数据的振幅&#xff0c;类似于分贝…

SpringSecurity-2.7中跨域问题

SpringSecurity-2.7中跨域问题 访问测试 起因 写这篇的起因是会了解到 SSM(CrosOrigin)解决跨域,但是会在加入SpringSecurity配置后,这个跨域解决方案就失效了,而/login这个请求上是无法添加这个注解或者通过配置(WebMvcConfig)去解决跨域,所以只能使用SpringSecurity提供的.c…