React 中ref 的使用(类组件和函数组件)以及forwardRef 与 useImperativeHandle 详解

news2024/11/27 12:56:43

前言

在一个父组件中,我们想要获取到其中的节点元素或者子组件实例,从而直接调用其上面的方法。Class 类组件和函数组件是两种不同的写法。

1. Class 组件中使用ref

在 React 的 Class 组件中,我们通过 createRef 创建 ref

class Parent extends React.Component {
    constructor(props) {
        super(props)
        this.inputRef = React.createRef();
    }
    
    componentDidMount() {
        console.log(this.inputRef)
        this.inputRef.current.focus();
    }
    render() {
        return <input ref={this.inputRef} />
    }
}

在上面的代码实例中,我们使用了 createRef 创建了一个 ref,将其挂到了原生DOM元素 input 上面,这时候,我们就可以通过 ref.current 获取到这个DOM元素,并且可以调用上面的方法。

ref 如果挂载到了一个Class 组件上面,ref.current 获取到的就是这个 Class 组件的实例,也可以访问该 Class组件的方法。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mhBeu0cR-1669790444660)(C:\Users\二三\AppData\Roaming\Typora\typora-user-images\image-20221130102359179.png)]

就拿上面这个页面来说,下面的【图纸计划】是一个子组件,当我们将 ref 挂载到该子组件中时,对应的父组件就可以通过 this.ref.current 调用该子组件上面的方法,比如下拉框展开的回调、下拉框值变化的回调等等。

2. 函数式组件

ref 回调函数 会在组件被挂载之后将组件实例传递给函数,函数组件不同于Class组件,函数组件没有实例。所以在正常情况下,ref 是不能挂载函数组件上的。

不过,函数组件是可以创建 ref 的,和类组件不同,函数组件使用 useRef .

import React, { memo, useEffect,  useRef, useState } from 'react';
const Parent = () => {
    const inputRef = useRef(null)
    input childRef = useRef(null)
    
    return (
        <>
        	<input ref={inputRef} onChange={onChange} />
        	<Child ref={childRef} handSave={handSave} />
        </>
    )
}

export default memo(Parent)

注意:这样的写法从语法上来说没问题,但是如果想在函数组件中通过 ref.current 获取 DOM元素或者子组件的实例,是拿不到的,需要其他的写法。

3. createRef 与 useRef 的区别

  • createRef 只能用于 Class 类组件中,useRef 只能用在函数式组件中。
  • createRef 每次渲染都会返回一个新的引用,useRef 每次都会返回相同的引用。
  • 如果函数式组件中使用 createRef 创建的 ref,其值会随着函数式组件的重新执行而不断初始化

4. 部分结论

综上所述,可以总结以下几点:

  • 原生 DOM 元素:ref的current 指向该节点
  • class 类组件:ref的current 指向该组件实例
  • 函数式组件:ref的current 指向 null,因为函数式组件没有实例。

那么对于函数式组件,我们真的没有办法获取并调用子组件上面的方法了吗?

答案:怎么可能,我们可以通过使用 forwardRef 来解决这个问题。

5. forwardRef

forwardRef (引用传递)是一种通过组件向子组件自动传递 引用 ref 的技术。

一句话概括:React 使用 forwardRef 完成 ref 的 透传,让函数式组件可以正常获取到子组件上面的方法。

5.1 代码写法

import React, { memo, forwardRef,  useRef, useImperativeHandle } from 'react';

const App = () => {
    const childRef = useRef(null)
    
    const getFocus = () => {
         // 调用子组件的方法
        childRef.current.inputFocus()
        // 也可以调用暴露出来的其他值
        childRef.current.setData(20)
    }
    
    return (
        <>
        	<Child ref={childRef}/>
        	<button onClick ={getFocus}>点击获取焦点<button/>
        </>
    )
}

// 子组件

const Child = forwardRef((props, ref) => {
    const inputRef = useRef(null)
    const [data, setData] = useState('10')
    
    // 使输入框获取焦点的方法
    const inputFocus = () => {
        inputRef.current.ocus()
    }
    // 输入框内容改变回调
    const changeValue = () => {
    	console.log('哈哈哈')
    }
    
    // 将该方法暴露给父组件
    useImperativeHandle(ref, () => ({
        inputFocus,
        changeValue,
        data,
        setData
    }))
    
    return <input  ref={inputRef} onChange={changeValue}>
});

5.2 forwardRef 解析

forwardRef 可以直接包裹一个函数式组件 ,被包裹的函数式组件会获得被分配给自己的ref(作为第二个参数)。如果直接将 ref 分配给没有被 forwardRef 包裹的函数式组件,React 会在控制台会报错。

forwardRef 会创建一个 React 组件,这个组件能够将其接受的 ref 属性转发到其组件树下的另一个组件中,也即是透传。

注意:forWardRef 的参数必须是 function

6. useImperativeHandle 解析

直接使用 forwardRef ,无法控制要暴露给父组件的值,所以我们使用 useImperativeHandle 来控制要将哪些东西暴露给父组件。

注意: useImperativeHandle 要与 forwardRef 一起使用。

调用方式如上面的代码实例所示。

6.1 传参

  • 第一个参数:ref:接收从 forwardRef 传递过来的 ref。
  • 第二个参数:createHandle:处理函数,返回值作为暴露给父组件的 ref 对象。
  • 第三个参数:deps:依赖项 deps,依赖项更改形成新的 ref 对象,可不传

useImperativeHandle 为我们提供了一个类似实例的东西,帮助我们通过 useImperativeHandle 的 第二个参数,将所返回的对象的内容挂载到附件的 ref.current 上

7. 总结

父、子组件再使用该 hooks 时步骤如下:

  1. 父组件使用 useRef 创建一个 ref 对象,将这个 ref 对象赋给子组件的 ref 属性。
  2. 子组件使用 forwardRef 包裹自己,允许作为函数组件的自己使用 ref。然后使用 useImperativeHandle 钩子函数,在该钩子函数的第二个函数参数中返回一些状态或方法,这个被返回的状态或方法就可以被父组件访问到。
  3. 父组件使用创建的 ref 对象的 current 属性获取子组件暴露出的状态或方法。

8. 特殊注意点:

在 函数式组件中使用 forwardRef useImperativeHandle ,有一个特殊的点需要注意:子组件的传参写法。如下所示:

// 可以使用这样的写法
    <Child ref={childRef} list={drawingPlanList} editable={!isDisabled}/>

// 但不用采用这样的写法:这样的 写法有问题,具体原因待排查

	const childProps = {
		ref: {childRef},
		list: {drawingPlanList},
		editable: {!isDisabled}
	}
    < Child {...childProps}/>

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

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

相关文章

传统制造企业进行数字化转型,是翻身还是翻船?

数实融合正在从可选项&#xff0c;变成每个行业都要面对的必选项&#xff0c;制造企业也从野蛮生长逐渐步入有序的数字化世界。 出品|产业家 2022年&#xff0c;疫情及经济环境全面淬炼了各行各业&#xff0c;大多数能有效应用数字化持续经营的企业成为幸存者&#xff0c;数字…

信号与进程间通信

目录结束进程结束后台进程结束前台进程信号基本概念接收信号发送信号代码演示接收信号函数&#xff08;signal&#xff09;SIG_IGNSIG_DFL自定义函数发送信号&#xff08;kill&#xff09;接收信号解决僵尸进程结束进程 结束后台进程 终端1&#xff1a;./main killed 终端2&a…

Linux系统编程(五)——Linux下的多线程

目录 0x01 线程概述 一、线程和进程的区别 二、线程和进程的虚拟地址空间 三、线程之间共享的非共享资源 四、NPTL 0x02 创建线程 0x03 终止线程 0x04 连接已终止的进程 0x05 线程的分离 0x06 线程取消 0x07 线程属性 0x08 线程同步 一、互斥锁 二、死锁 三、如何…

三肽Gly-Cys-Gly、88440-55-5

三肽Gly-Cys-Gly 编号&#xff1a;111774 CAS号&#xff1a;88440-55-5 三字母&#xff1a;H2N-Gly-Cys-Gly-COOH 描 述&#xff1a;羧肽酶 U 抑制剂&#xff08;凝血酶可激活的纤维蛋白溶解抑制剂&#xff0c;TAFI&#xff09;&#xff0c;Ki 0.14 μM。编号: 111774 中文名称…

Java#29(集合进阶2---双列集合)

目录 一.Map------------双列集合-----------一次添加一对元素,如: 小明 99岁 1.特点: 2.Map中常见的API 3.Map集合的遍历方式 4.HashMap 5.LinkedHashMap 6.TreeMap 一.Map------------双列集合-----------一次添加一对元素,如: 小明 99岁 1.特点: (1)双列集合一次需要…

在嵌入式里面实现printf()类似的功能

学习C语言大多数都是从printf("hello world")开始的&#xff0c;对于printf的熟悉程度最高&#xff0c;在嵌入式编程中&#xff0c;实现printf函数有一种很标准的办法就是实现putch&#xff0c;绑定对应的串口输出&#xff0c;设置好波特率&#xff0c;使能串口就可以…

No module named ‘PyQt5.QtWebEngineWidgets‘kn-----已解决

1.情况说明 本人在学习PyQt5的时候遇到了 from PyQt5.QtWebEngineWidgets import * 报错的情况&#xff0c;原因就是ModuleNotFoundError: No module named PyQt5.QtWebEngineWidgets 没有PyQt5.QtWebEngineWidgets&#xff0c; 2.解决办法&#xff1a; 解决办法一&#xf…

最快最便捷的pytest使用allure测试报告

一、前言 最近通过群友了解到了allure这个报告&#xff0c;开始还不以为然&#xff0c;但还是逃不过真香定律。 经过试用之后&#xff0c;发现这个报告真的很好&#xff0c;很适合自动化测试结果的展示。下面说说我的探索历程吧。 选用的项目为Selenium自动化测试Pytest框架…

Day1使用Burpsuite抓包工具抓包,改变UA头使得手机和pc端界面互相转换

1.前期工作&#xff1a;安装Burpsuite工具这里网上有许多教程&#xff0c;大致步骤如下&#xff1a; 找到安装包然后解压缩&#xff0c;然后双击 安装jdk&#xff0c;然后就是配置环境变量&#xff0c;如果是默认jdk安装路径没有更改路径的话就是跟如下一样 配置如下&#xff…

跨平台开发方案的三个时代

跨平台开发从本质上讲是为了增加业务代码的复用率&#xff0c;减少因为要适配多个平台带来的工作量&#xff0c;从而降低开发成本。在提高业务专注度的同时&#xff0c;能够为用户提供一致的用户体验&#xff0c;实现“多快好省”的效果。 跨平台是跨哪些平台&#xff1f;怎么…

高视医疗在港交所招股:IPO募资要用于贷款,高铁塔为控股股东

11月30日&#xff0c;高视医疗&#xff08;HK:02407&#xff09;在港交所发布公告&#xff0c;拟全球发售1306.86万股股份&#xff0c;其中香港发售股份130.7万股&#xff0c;国际发售股份1176.16万股&#xff0c;另有15%超额配股权&#xff0c;于2022年11月30日至12月5日招股&…

Talk预告 | 亚马逊云科技上海人工智能研究院肖天骏:基于视频的自监督物体遮挡补全分割

本期为TechBeat人工智能社区第458期线上Talk&#xff01; 北京时间11月30日(周三)20:00&#xff0c;亚马逊云科技上海人工智能研究院资深应用科学家——肖天骏的Talk将准时在TechBeat人工智能社区开播&#xff01; 他与大家分享的主题是: “基于视频的自监督物体遮挡补全分割”…

请求和响应

目录1 请求对象1.1 请求对象介绍1.2 请求对象常用方法-获取各自路径1.3 请求对象常用方法-获取请求头信息1.4 请求对象常用方法-请求参数信息1.5 获取请求参数并封装对象1.5.1 手动封装方式1.5.2 反射封装方式1.5.3 工具类封装方式1.6 流对象获取请求信息1.7 中文乱码问题1.8 请…

可发生点击化学反应:1458576-00-5,Biotin-PEG4-alkyne,生物素-四聚乙二醇-炔

【中文名称】生物素-四聚乙二醇-炔&#xff0c;生物素-四聚乙二醇-丙炔基 【英文名称】 Biotin-PEG4-alkyne 【货号】Y-PE-2172 【CAS】1458576-00-5 【分子式】C21H35N3O6S 【分子量】457.58 【基团】alkyne 【纯度】95% 【规格】25mg&#xff0c;100mg&#xff0c;250mg 【是…

如何应对继承的双面性

如何应对继承的双面性 继承既强大又有破坏性&#xff0c;那怎么办呢&#xff1f; 1&#xff09;避免使用继承&#xff1b; 2&#xff09;正确使用继承。 我们先来看怎么避免继承&#xff0c;有三种方法&#xff1a; 使用final关键字&#xff1b; 优先使用组合而非继承&#…

11月30日:linux服务器安装以及部署项目

准备一个连接linux服务器的可视化工具&#xff0c;开始发车 推荐使用国产&#xff1a; finalshell 下载地址&#xff1a;FinalShell SSH工具,服务器管理,远程桌面加速软件,支持Windows,macOS,Linux,版本3.9.7,更新时间2022.10.26 - SSH工具 SSH客户端 xshell&#xff1a;安装…

Spring Cloud Gateway微服务网关快速入门

介绍 Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0&#xff0c;Spring Boot 2.0 和 Project Reactor 等技术开发的网关&#xff0c;Spring Cloud Gateway 旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式。Spring Cloud Gateway 作为 Spring Cloud 生态…

Kamiya丨Kamiya艾美捷人乳铁蛋白ELISA说明书

Kamiya艾美捷人乳铁蛋白ELISA预期用途&#xff1a; 人乳铁蛋白ELISA是一种高度灵敏的双位点酶联免疫测定&#xff08;ELISA&#xff09;人类生物样品中乳铁蛋白的测定。仅供研究使用。不用于诊断程序。 引言 乳铁蛋白&#xff08;LF&#xff09;是一种具有抗菌活性的多功能铁…

运动“双十一”持续走热,缤跃酒店洞察市场需求,创新打造运动健康酒店!

2022年“双十一”购物促销活动刚刚结束&#xff0c;各大品牌陆续开始展示肌肉&#xff0c;在众多数据中运动健身领域相关数据不容忽视&#xff0c;居家健身器械等商品持续走热&#xff0c;户外运动设备销售量也保持热度。由此可见&#xff0c;在当下全民运动热潮下&#xff0c;…

uniapp开发微信小程序实现语音识别,使用微信同声传译插件,

第一步&#xff1a;在微信小程序管理后台&#xff1a;“设置”-》“第三方设置”-》“插件管理”中添加插件。 但是这个地方&#xff0c;没有搜索到插件&#xff0c;就到微信服务市场 搜索到以后添加到需要的小程序里面&#xff0c;然后返回管理中心查看&#xff0c;就可以看…