React中ref是一个对象,它有一个current属性,可以对这个属性进行操作,用于获取DOM元素和保存变化的值。什么是保存变化的值?就是在组件中,你想保存与组件渲染无关的值,就是JSX中用不到的或不显示到页面上的值,比如setTimeout的返回的ID,就可以把这个值放到ref中。为什么要放到ref中,因为更改ref的值,不会引起组件的重新渲染,因为值与渲染无关,它也不应该引起组件渲染。怎么获取ref对象呢?调用useRef()函数和createRef()函数,它们返回ref对象。在组件的整个生命周期中,ref对象一直存在。组件创建,更准确地说法是,组件挂载,ref对象创建,组件销毁,ref对象销毁。

  useRef是一个React Hooks,在函数组件中使用,它还可以接受一个参数,用于初始化useRef返回的对象的current属性。

const ref = useRef(initialValue);

  使用useRef获取DOM元素,就是把ref对象赋给react element的ref属性, 每一个react element都有一个ref属性。组件挂载后,ref对象的current属性,就自动指向DOM元素

import React, { useRef } from "react";

const CustomTextInput = () => {
const textInput = useRef(); const focusTextInput = () => textInput.current.focus(); return (
<>
<input type="text" ref={textInput} />
<button onClick={focusTextInput}>Focus the text input</button>
</>
);
}

  组件挂载完成,textInput.current指向input输入框,就可以直接调用输入框的focus方法。

  使用useRef保存变量的值,直接把变量或值,赋值给ref对象的current属性就可以了。

import React, { useRef, useEffect } from "react";

const Timer = () => {
const intervalRef = useRef(); useEffect(() => {
const id = setInterval(() => {
console.log("A second has passed");
}, 1000); intervalRef.current = id; return () => clearInterval(intervalRef.current);
}); const handleCancel = () => clearInterval(intervalRef.current); return (
<>
//...
</>
);
}

  这里要注意的是更新ref对象的值,是一个side effect,因为这个值不参与渲染,更新值是React渲染之外,要做的事情,所以要放到useEffect或useLayoutEffect中,放到事件处理函数中也可以。如果以下有代码

import React, { useRef } from "react";

const RenderCounter = () => {
const counter = useRef(0);
counter.current = counter.current + 1; return (
<h1>{`The component has been re-rendered ${counter} times`}</h1>
);
};

  最好改成   

import React, { useRef } from "react";

const RenderCounter = () => {
const counter = useRef(0); useEffect(() => {
counter.current = counter.current + 1;
}); return (
<h1>{`The component has been re-rendered ${counter} times`}</h1>
);
};

  函数组件中也可以使用createRef, 但当使用createRef时,每一次组件渲染时都会创建全新的ref对象,而不是每一次渲染都共用一个ref对象,性能会有问题,再说useRef就是代替createRef的,所以在函数组件中就没有必要使用createRef了。

  其实,使用useRef,也可以获取到子组件,直接调用子组件中的方法,不过就是用点麻烦,因为ref只能获取到类组件的实例,也只有类才有实例。函数组件是没有实例的,怎么获取到它?使用forwardref, 把一个函数组件包起来,函数组件就多了一个ref属性。子组件中用useImperativeHandle暴露方法。结合forwardRef 和useImperativeHandle。使用create-react-app 创建React项目,在src中创建一个Counter组件

import React from 'react';
import { useState } from "react" const Counter = () => {
const [count, setCount] = useState(0); const clickHandler = () => {
setCount(c => c + 1);
} return (
<p>count is {count} </p>
)
} export default Counter;

  然后在App.js中引入

import React from 'react';
import Counter from "./Counter"; function App() {
return (
<React.Fragment>
<Counter></Counter>
<button>Add</button>
</React.Fragment>
);
} export default App;

  此时,如果想点击父组件App中的button来增加子组件的count,怎么办?首先,子组件Counter,要把clickHandler方法暴露出来。做法,1,export的不是组件了,而是forwardRef(组件); 2,组件要接受参数ref,const Counter = (props, ref); 3,  在组件内部,使用useImperativeHandle,它的第一个参数是ref,第二个参数是回调函数,返回一个对象,对象中的属性和方法,就可以在父组件中使用ref获取到。

import React, { forwardRef, useImperativeHandle } from 'react';
import { useState } from "react" // 组件被forwardRef之后,组件多了一个ref属性
const Counter = (props, ref) => {
const [count, setCount] = useState(0); const clickHandler = () => {
setCount(c => c + 1);
}
// 第一个参数就是ref,暴露出click方法,供父组件使用
useImperativeHandle(ref, () => {
return ({
click: clickHandler
})
}) return (
<p>count is {count} </p>
)
} // export forwardRef(组件)
export default forwardRef(Counter);

  其次,在父组件App中使用ref,引用子组件,并在button的click回调函数中使用ref

function App() {
const counteRef = useRef(); const handleClick = () => {
counteRef.current.click();
} return (
<React.Fragment>
<Counter ref={counteRef}></Counter>
<button onClick={handleClick}>Add</button>
</React.Fragment>
);
}

  如果项目中使用了Redux和React-Redux,子组件export的是connect()(组件),是一个高阶组件,那父组件中怎么引用子组件呢?如果是react-redux 6.0以前的版本,connect函数第四个参数设置为{ withRef: true },父组件getWrappedInstance()就可以获取到包裹的子组件

connect(null, null, null, { withRef: true })(组件);

  如果是react-redux 6.0 以后的版本,使用 forwardRef: true , 代替{ withRef: true },父组件中的ref可以直接获取到包包裹的子组件

connect(null, null, null, { forwardRef: true })(组件);

  

React中的Ref的更多相关文章

  1. react中的ref的3种方式

    2020-03-31 react中的ref的3种方式 react中ref的3种绑定方式 方式1: string类型绑定 类似于vue中的ref绑定方式,可以通过this.refs.绑定的ref的名字获 ...

  2. react中的ref在input中的详解

    当我们在项目中遇见文本输入框的时候,获取时刻输入框中的值 1.受控组件 class NameForm extends React.Component { constructor(props) { su ...

  3. 六、React 键盘事件 表单事件 事件对象以及React中的ref获取dom节点 、React实现类似Vue的双向数据绑定

    接:https://www.cnblogs.com/chenxi188/p/11782349.html 事件对象 .键盘事件. 表单事件 .ref获取dom节点.React实现类似vue双向数据绑定 ...

  4. 【Web技术】401- 在 React 中使用 Shadow DOM

    本文作者:houfeng 1. Shadow DOM 是什么 Shadow DOM 是什么?我们先来打开 Chrome 的 DevTool,并在 'Settings -> Preferences ...

  5. React中ref的使用方法

    React中ref的使用方法 在react典型的数据流中,props传递是父子组件交互的唯一方式:通过传递一个新的props值来使子组件重新re-render,从而达到父子组件通信.当然,就像reac ...

  6. vue中:key 和react 中key={} 的作用,以及ref的特性?

    vue中:key 和react 中key={} 为了给 vue 或者react 一个提示,以便它能跟踪每个节点的身份,从而重用和重新排序现有元素,你需要为每项提供一个唯一 key 属性 一句话概括就是 ...

  7. React中Ref 的使用 React-踩坑记_05

    React中Ref 的使用 React v16.6.3 在典型的React数据流中,props是父组件与其子组件交互的唯一方式.要修改子项,请使用new props 重新呈现它.但是,在某些情况下,需 ...

  8. React 中阻止事件冒泡的问题

    在正式开始前,先来看看 JS 中事件的触发与事件处理器的执行. JS 中事件的监听与处理 事件捕获与冒泡 DOM 事件会先后经历 捕获 与 冒泡 两个阶段.捕获即事件沿着 DOM 树由上往下传递,到达 ...

  9. react中input自动聚焦问题

    input自动聚焦问题 在react中可以使用refs解决这个问题,首先看一下refs的使用场景: (1)处理焦点.文本选择或媒体控制. (2)触发强制动画. (3)集成第三方 DOM 库. 使用re ...

  10. react中直接调用子组件的方法(非props方式)

    我们都知道在 react中,若要在父组件调用子组件的方法,通常我们会采用在父组件定义一个方法,作为props转给子组件,然后执行该方法,可以获取到子组件传回的参数以得到我们的目的. 显而易见,这个执行 ...

随机推荐

  1. 第五章-WAF 绕过

    WAF 绕过 1.WAF分类 1.1.软件 WAF 一般被安装到 Web 服务器中直接对其进行防护,能够接触到服务器上的文件,直接检测服务器上是否有不安全的文件和操作等. 常见的软件:安全狗.云盾.云 ...

  2. Masscan入门手册

    相关文章 https://www.cnblogs.com/huim/p/12116004.html https://4hou.win/wordpress/?cat=3080 Nmap vs Massc ...

  3. 解决:Failed to get D-Bus connection: Operation not permitted

    docker中安装完httpd服务后,使用命令systemctl start httpd.service,发现报错,错误信息:Failed to get D-Bus connection: Opera ...

  4. gin 单个文件函数 上传文件到本地目录里

    // 单个文件 上传文件到本地目录里 // 调用方法 utils.UplaodFileToLocal(c) // author haima func UplaodFileToLocal(c *gin. ...

  5. c语言编译系统工作原理

    c语言编译系统内部的工作原理 程序生命周期概述 一个程序的生命周期可以被分成四个部分: 创建 编译 运行 退出 以一个简单的 helloworld.c 程序为例: #include<stdio. ...

  6. k8s多集群切换:使用kubeconfig文件管理多套kubernetes(k8s)集群

    目录 一.系统环境 二.前言 三.kubeconfig文件 四.kubernetes(k8s)多集群切换 一.系统环境 服务器版本 docker软件版本 CPU架构 CentOS Linux rele ...

  7. etcd MVCC 存储结构及流程

    什么是 MVCC MVCC 是 Multi-Version Concurrency Control 的缩写,即多版本并发控制.它是一种并发控制的方法,用于在数据库系统中实现事务的隔离性.MVCC 是一 ...

  8. Pandas学习之路【3】

    新增列的一些操作 1.新增一个列,直接给列赋值 # 取所有行,新增的列为new_col df.loc[:, 'new_col'] = 100 2.使用df.apply方法给新增的列赋值 def get ...

  9. github加速与添加ssh密钥

    part1-github加速 此处推荐Fetch GitHub Hosts,文章的中间位置有手动添加dns的内容,十分完备,此处不赘述.不知道是不是我家网络抽风,总是得代理才能进githubQAQ难受 ...

  10. 8.11考试总结(NOIP模拟36)[Dove 打扑克·Cicada 与排序·Cicada 拿衣服]

    我会化作人间的风雨陪在你的身边 T1 Dove 打扑克 解题思路 考场上是想了一个树状数组维护的打法,但是竟然和 \(qn^2\) 的算法一样是 65pts 暴力就是对于每一次 2 询问重新建一下树状 ...