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. 14、web 中间件加固-Tomcat 加固

    1.用户配置 如果不需要控制台管理,请更改控制台用户文件注销账号信息:如果需要,请更改账户信息 修改 tomcat/conf/tomcat-user.xml 文件 注释或修改如下信息 <role ...

  2. 【OpenVINO™】基于 C# 和 OpenVINO™ 部署 Blazeface 模型实现人脸检测

     前言 OpenVINO C# API 是一个 OpenVINO 的 .Net wrapper,应用最新的 OpenVINO 库开发,通过 OpenVINO C API 实现 .Net 对 OpenV ...

  3. Swift Charts 报错:Initializer ... requires that .. conform to ‘Identifiable‘

    目录 1. 问题描述 2. 解决办法 1. 问题描述 在运行Swift Charts官方折线图示例时,出现了如下错误. Initializer 'init(_:content:)' requires ...

  4. docker-compose 安装redis

    一. docker 拉去最新版本的redis `docker pull redis:6.0.6` #后面可以带上tag号, 默认拉取最新版本 二. docker安装redis 执行命令: mkdir ...

  5. WEB服务与NGINX(8)-NGINX的长连接功能

    1. 长连接配置 keepalive_timeout; 定义客户端保持连接超时时长,0表示禁止长连接,默认为65s,建议使用15s即可. 在ngx_http_upstream_module中也有此项设 ...

  6. log4j的配置详解

    参考文章:https://www.jianshu.com/p/ccafda45bcea 引入log4j: 在项目中单独使用log4j进行日志的输出: maven依赖: <dependency&g ...

  7. CentOS7部署Redis(离线单机)

    一.检查是否安装 ## 检查是否安装了Redis [root@localhost /]# ps -ef | grep redis ## 存在就删除 [root@localhost /]# sudo y ...

  8. EDP .Net开发框架--权限

    平台下载地址:https://gitee.com/alwaysinsist/edp 权限介绍 权限实际上就是谁有权使用或是访问什么,这里的"谁"可以视作"授权对象&quo ...

  9. Spring6 对 集成MyBatis 开发运用(附有详细的操作步骤)

    1. Spring6 对 集成MyBatis 开发运用(附有详细的操作步骤) @ 目录 1. Spring6 对 集成MyBatis 开发运用(附有详细的操作步骤) 每博一文案 2. 大概的实现步骤概 ...

  10. Redis高可用一(主从)

    Redis高可用一(主从) 1.首先要有2或以上的Redis数据库,我这里三个redis(一主两从) 2.进入到主Redis 配置 redis.conf文件 # 主redis bind 0.0.0.0 ...