React Hooks—— useRef用法及类组件的三种ref创建使用方式

  • Post author:
  • Post category:其他




useRef



ref使用场景


通常,当你的组件需要“跳出” React 并与外部 API 通信时,你会用到 ref —— 通常是不会影响组件外观的浏览器 API。以下是这些罕见情况中的几个:


  • 存储

    timeout ID


  • 存储和操作

    DOM 元素

    ,涉及非受控组件。赋值给标签,目的是获取DOM元素;赋值给类组件,目的是获取组件的实例;

  • 存储不需要被用来计算 JSX 的其他对象。


如果你的组件需要存储一些值,但不影响渲染逻辑,请选择 ref。



类组件的三种创建和使用方式



方式一——字符串形式的ref(

过时了,未来版本可能移除,开发时不推荐使用,效率低



通过

this.refs

访问

<input ref="input1"/>


方式二——回调形式的ref
React帮我们调用回调函数,并且将当前的DOM节点传入回调函数
<input ref={(currentNode) => {this.input1 = currentNode}} />
// currentNode是当前所处的节点,也就是这个input


不能通过

this.refs

访问,因为这些ref都成了实例属性


React不会帮你执行未知属性的回调函数

<input ref={c => this.input1 = c } ahh={() => {console.log(1)}} type="text" placeholder="点击按钮提示数据"/>

如果ref回调函数是以

内联函数

的形式定义的,在更新过程中它会被执行两次,

第一次传入参数null,第二次传入参数DOM元素

。这是因为在每次渲染时创建一个新的实例,所以React清空旧的ref并设置新的。通过

将ref的回调函数定义成class的绑定函数

的方式可以避免上述问题,但是

大多数情况下它是无效的(无关紧要)



方式三——createRef创建ref容器(

最推荐



React.createRef调用后可以返回一个容器,该容器可以存储被ref所标识的节点,该容器是“专人专用”的,多个ref的值可以相互独立


ref是实例的属性,不能通过this.refs访问


this.myRef.current是当前引用的DOM,this.myRef.current.value是当前DOM的值

myRef = React.createRef();
<input ref={this.myRef} />



hooks组件通过useRef创建ref对象


在函数组件中,可以基于


useRef

获取DOM元素!类似于类组件中的 :


  • ref={x=>thix.box=x}

  • React.createRef


函数组件中创建ref对象的两种方法:


  • let box1 = useRef(null)

  • let box2 = React.createRef();


注意:


React.createRef也是ref对象,在类组件和函数组件中都可以使用


useRef只能在函数组件中使用,所有的hooks函数都只能在函数组件中使用,在类组件中使用会报错


ref只能在DOM创建之后才能获取DOM元素,也就是说在useLayoutEffect阶段就可以使用



createRef性能比useRef差——每次渲染创建新ref对象


  • createRef 每次渲染都会返回一个新的引用

  • 而 useRef 每次都会返回相同的引用
import React, { useState, useEffect, useRef } from "react";
import { Button } from 'antd';
import './Demo.less';

let prev1,
    prev2;
const Demo = function Demo() {
    let [num, setNum] = useState(0);
// 函数组件中创建ref对象的两种方法
    let box1 = useRef(null),
        box2 = React.createRef();
    if (!prev1) {
        // 第一次DEMO执行,把第一次创建的REF对象赋值给变量
        prev1 = box1;
        prev2 = box2;
    } else {
        // 第二次DEMO执行,我们验证一下,新创建的REF对象,和之前第一次创建的REF对象,是否一致?
        console.log(prev1 === box1); //true  useRef再每一次组件更新的时候(函数重新执行),再次执行useRef方法的时候,不会创建新的REF对象了,获取到的还是第一次创建的那个REF对象!!
        console.log(prev2 === box2); //false createRef在每一次组件更新的时候,都会创建一个全新的REF对象出来,比较浪费性能!!
        // 总结:在类组件中,创建REF对象,我们基于 React.createRef 处理;但是在函数组件中,为了保证性能,我们应该使用专属的 useRef 处理!!
    }

    useEffect(() => {
        console.log(box1.current);
        console.log(box2.current);
    });

    return <div className="demo">
        <span className="num" ref={box1}>{num}</span>
        <span className="num" ref={box2}>哈哈哈</span>
        <Button type="primary" size="small"
            onClick={() => {
                setNum(num + 1);
            }}>
            新增
        </Button>
    </div>;
};


总结:在类组件中,创建Ref对象,基于React.createRef处理;在函数组件中为了保证性能使用useRef



ref的DOM用法总结【useRef】


  • 给元素标签设置ref,目的:获取对应的DOM元素


  • 给类组件设置ref,目的:获取当前调用组件创建的实例(后续可以根据实例获取子组件中的相关信息)

    // 基于ref获取子组件的实例,这样基于实例,可以调用子组件内部,挂载到实例上的东西
    class Child extends React.Component {
        state = { x: 1000 };
        render() {
            return <div className="child-box">
                {this.state.x}
            </div>;
        }
    } 
    

  • 给函数组件/hooks设置ref,直接报错:Function components cannot be given refs. Attempts to access this ref wil fail,但是可以配合React.forwardRef实现ref的转发。目的:获取函数子组件内的某个DOM元素

    // 基于forwardRef实现ref转发,目的:获取子组件内部的某个元素
    const Child = React.forwardRef(function Child(props, ref) {
        // console.log(ref); //在DEMO中,调用Child的时候,传递的ref对象「x」
        return <div className="child-box">
            <span ref={ref}>哈哈哈</span>
        </div>;
    }); 
    



版权声明:本文为m0_46569480原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。