首页 > 其他分享 >React 中ref 的使用

React 中ref 的使用

时间:2023-02-02 11:33:05浏览次数:41  
标签:current useRef 函数 React forwardRef 使用 组件 ref

场景:

类组件、函数组件、

forwardRef、useImperativeHandle 详解

 

前言

在一个父组件中,我们想要获取到其中的节点元素或者子组件实例,从而直接调用其上面的方法。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组件的方法

就拿上面这个页面来说,下面的【图纸计划】是一个子组件,当我们将 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}/>

 

标签:current,useRef,函数,React,forwardRef,使用,组件,ref
From: https://www.cnblogs.com/cczlovexw/p/17085491.html

相关文章

  • 如何使用Tomcat自带的日志实现tomcat-juli.jar
    前言Tomcat自带的日志实现是​​tomcat-juli.jar​​,它是对默认的JDK日志java.util.logging进行一定的封装,和标准JDK日志支持相同的配置,但是和log4j等常用的日志框架比起来......
  • caddy 部署pwa 组合使用 虚拟目录
        xxx.com:12345{encodegzipzstdhandle/wjy/*{#反省代理请求xxx.com:12345/wjy/xxxx到127.0.0.1:9999/wjy/xxxxreverse_proxy1......
  • 技术汇总:第一章:使用poi实现表单下载成xls文件并打印
    业务需求:点击下载第一种方式:实现代码@RequestMapping("/ad/downExcel")publicStringdownExcel(HttpSessionsession,HttpServletResponseresponse)......
  • Java之使用zxing.jar包生成二维码
    由于时代科学的进步,二维码已经和我们的生活密不可分,在开发过程中往往会涉及到和二维码相关的开发,今天这篇文章就教会大家如何使用zxing.jar包生成二维码下面这个就是......
  • location.href的用法
    https://blog.csdn.net/qiushisoftware/article/details/80599547window.location.href:"url",在本页跳转到url所指的链接window.location.replace:"url",用新的url替换原......
  • UE4如何使用另一个项目工程中的插件
    应用场景:由于本人去年刚接触UE4,里面有太多不懂的东西,先记在这里。一个现有的项目中只用的TCP/UDP通信的插件,我想搬到自己新建的项目中改如何做呢?1、新建插件目录打开......
  • [转]windows下nacos的配置使用
    原文地址:NacosWindows版本安装-简书(jianshu.com)1.从github下载zip,地址:https://github.com/alibaba/nacos/releases2.解压,执行conf目录的sql文件建立需要用到......
  • 使用kubeadm安装k8s1.26.0笔记2
    一.安装版本Kubeadm使用cni方式安装版本:v1.26.0 二.机器准备1.机器规格本次安装1个master和1个node节点Master:192.168.64.6Node:192.168.64.7规则:CPU:2内存:4G系......
  • Latex Referece标题去掉,保留引用
    1.LaTeX为了节省空间,我们有时需要保留引用,但会去掉标题Reference保留空间\patchcmd{\thebibliography}{\section*{\refname}}{}{}{}使用上面一句就可以了2.LaTeXR......
  • 一篇文章带你了解KendoReact DateRangePicker,让日期选择变得更酷炫!
    KendoUI致力于新的开发,来满足不断变化的需求。现在我们非常自豪地宣布,通过React框架的KendoUIJavaScript封装来支持ReactJavascript框架。KendoReact能够为客户提供更......