当前位置:   article > 正文

[React] ref属性

[React] ref属性

简介

ref 即 reference ,是 React 提供给我们的安全访问 DOM 元素或者某个组件实例的句柄。
组件被调用时会新建一个该组件的实例,而 ref 就会指向这个实例。它可以是一个回调函数,这个回调函数会在组件被挂载后立即执行。
为了防止内存泄漏,当卸载一个组件的时候,组件里所有的 ref 都会变为 null。

ref的创建

所谓 ref 对象的创建,就是通过 React.createRef 或者 React.useRef 来在组件中创建一个 ref 原始对象,用来获取dom元素。

{
    current:null , // current指向ref对象获取到的实际内容,可以是dom元素,组件实例,或者其它。
}
  • 1
  • 2
  • 3

类组件

在类组件上通过createRef创建ref对象。

class Test extends React.Component{
    constructor(props){
       super(props);
       this.currentRef = React.createRef(null);
    }
    componentDidMount(){
        console.log(this.currentRef);
    }
    render= () => <div ref={ this.currentRef } >createRef test</div>
}
export default Test;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在这里插入图片描述

createRef 就是创建了一个对象,对象上的 current 属性,用于保存通过 ref 获取的 DOM 元素,组件实例等。

// react/src/ReactCreateRef.js
export function createRef() {
  const refObject = {
    current: null,
  }
  return refObject;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

createRef 一般用于类组件创建 ref 对象,可以将 ref 对象绑定在类组件实例上,这样更方便后续操作 ref
注意:不要在函数组件中使用 createRef,否则会造成 ref 对象内容丢失等情况。

函数组件

在函数组件中通过 useRef 来创建 ref 对象。

export default function Test() {
  const currentRef = React.useRef(null)
  React.useEffect(() => {
    console.log(currentRef.current)
  }, []);
  return <div ref={ currentRef } >useRef test</div>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

在这里插入图片描述

useRef 底层逻辑是和 createRef 差不多,就是 ref 保存位置不相同。类组件有一个实例 instance 能够维护像 ref 这种信息,所以createRef是直接把ref对象存储在 instance 上的,而函数组件没有这种instance,如果在函数组件上使用createRef,那么每一次函数组件更新,所有变量都会重新声明,那么ref就会随之被重置,起不到保存的效果了,这就是函数组件为什么不能用 createRef 的原因。
为了解决这个问题,hooks 和函数组件对应的 fiber 对象建立起关联,将 useRef 产生的 ref 对象挂到函数组件对应的 fiber 上,函数组件每次执行,只要组件不被销毁,函数组件对应的 fiber 对象一直存在,所以 ref 等信息就会被保存下来。

ref作为属性

react 对 ref 的处理,主要表现在父子组件交互时,处理标签中的 ref 属性,以及转发 ref

类组件

ref属性是一个字符串(已废弃,不建议使用)

在类组件中,用字符串 ref 标记一个 DOM 元素或一个类组件,在react 在底层逻辑会判断类型,如果是 DOM 元素,会把真实 DOM 绑定在组件 this.refs (组件实例下的 refs )属性上,如果是类组件,会把子组件的实例绑定在 this.refs 上。

class Children extends React.Component {
  render = () => <div>hello,world</div>
}

export default class Test extends React.Component {
  componentDidMount() {
    console.log(this.refs) // 
  }
  render = () => <div>
    <div ref="currentDom">ref是字符串</div>
    <Children ref="currentComInstance" />
  </div>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

在这里插入图片描述
上述代码中,取值时可以用this.ref.currentDomthis.ref.currentComInstance取到相应的元素。
这种方式已经废弃了,原因有以下几点:

  1. string ref 不可组合,如果第三方库的父组件已经给子组件传递了 ref,那么我们就无法再在子组件上添加 ref
  2. 回调引用没有一个所有者,因此您可以随时编写它们
  3. 不适用于Flow之类的静态分析
  4. string ref 强制React跟踪当前正在执行的组件

ref 属性是一个函数

class Children extends React.Component {
  render = () => <div>hello,world</div>
}

export default class Test extends React.Component {
  currentDom = null
  currentComponentInstance = null
  componentDidMount() {
    console.log(this.currentDom)
    console.log(this.currentComponentInstance)
  }
  render = () => <div>
    <div ref={(node) => this.currentDom = node}  >ref是函数</div>
    <Children ref={(node) => this.currentComponentInstance = node} />
  </div>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

在这里插入图片描述
如上述代码所示,当用一个函数来标记 ref 的时候,将作为 callback 形式,等到真实 DOM 创建阶段,执行 callback ,获取的 DOM 元素或组件实例,再以回调函数第一个参数形式返回。所以可以像所以在上述代码中,用组件实例下的属性 currentDomcurrentComponentInstance 来接收真实 DOM 和组件实例。

ref属性是一个ref对象

创建ref对象,如第一部分所述,使用createRef

class Children extends React.Component {
  render = () => <div>hello,world</div>
}
export default class Test extends React.Component {
  currentDom = React.createRef(null)
  currentComponentInstance = React.createRef(null)
  componentDidMount() {
    console.log(this.currentDom)
    console.log(this.currentComponentInstance)
  }
  render = () => <div>
    <div ref={this.currentDom}>ref是对象</div>
    <Children ref={this.currentComponentInstance} />
  </div>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

打印
以上三种方法都无法用在函数组件中,因为函数组件没有实例。

函数组件

不能在函数组件上直接使用 ref 属性,因为他们没有实例,这时需要用到forwardRef

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

const Child = React.forwardRef((props, ref) => {
  return <input ref={ref}></input>
})

const Test = () => {
  const childrenRef = useRef();
  useEffect(() => {
    console.log(childrenRef.current); // child input
  }, [])
  return <Child ref={childrenRef}></Child>
}
export default Test;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

在这里插入图片描述
React.forwardRef返回的是一个react组件,接受的参数是一个render函数,render(props, ref)。这个函数的第二个参数会将接收到的ref作为返回组件的ref属性,这就实现了ref的转发。

ref作用

其实ref是不推荐使用的,因为使用ref后,一些情况下元素会脱离react的控制。

受控组件与非受控组件

受控组件

受控组件指的是,在表单控件(例如input等),其值是受到react管理和控制的,即仅需要定义一个state和setState函数,在用户输入时UI和值都可以实时更新。

import React, { useState } from 'react';

function ControlledComponentExample() {
  const [inputValue, setInputValue] = useState('');

  const handleInputChange = (event) => {
    setInputValue(event.target.value);
  };

  return (
    <div>
      <label htmlFor="input">Enter something: </label>
      <input
        type="text"
        id="input"
        value={inputValue}
        onChange={handleInputChange}
      />
      <p>You typed: {inputValue}</p>
    </div>
  );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

上述代码流程为,当input中值发生改变时,触发handleInputChange函数,从而将event.target.value更新到inputValue上,state的更新触发了页面的重新渲染,所以页面也更新了。
受控组件的特点时,数据流是单向的,均由state变化而触发,所以不应该强制赋值。

非受控组件

非受控组件即通过ref直接取dom元素中的值,而不是通过state控制它。

import React, { useRef } from 'react';

function UncontrolledComponentExample() {
  const inputRef = useRef(null);

  const handleButtonClick = () => {
    alert('Input value is: ' + inputRef.current.value);
  };

  return (
    <div>
      <label htmlFor="uncontrolledInput">Enter something: </label>
      <input
        type="text"
        id="uncontrolledInput"
        ref={inputRef}
      />
      <button onClick={handleButtonClick}>Get Input Value</button>
    </div>
  );
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

点击按钮的时候,handleButtonClick函数中直接通过ref拿取input中的值,不通过react自身的state变换。

useRef

在函数组件中,useRef 返回一个可变的 ref 对象,返回的 ref 对象在函数组件的整个生命周期内保持不变。所以 useRef 可以很方便地保存任何可变值。

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

export default function Test() {
  const ref = useRef(false)
  const [a, setA] = useState(0);

  useEffect(() => {
    if (!ref.current) {
      ref.current = true
    } else {
      //do some
    }
  }, [a]);
  return ('');
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/65804
推荐阅读
相关标签
  

闽ICP备14008679号