React Ref的用法

React Ref的用法

看到React 17中ref的用法发生了一些变化,记录一下。这块内容在官网上是Ref转发和Refs & DOM的内容。

看到React 17中ref的用法发生了一些变化,记录一下。

这块内容在官网上是Ref转发Refs & DOM的内容。

ref

ref这个东西有点反向引用的感觉。比较好的理解是ref={}这个写法,类似于把自定义的变量或者说标记打到组件或者DOM元素上去,这样标记的current属性就是被打标记的组件或者DOM元素。

要注意的是,ref和key一样,并不是普通的props属性,react对其进行了单独的处理。

ref提供了一种类似于反向引用的操作,有点别扭,也有固定的写法。官网推荐了一些适合ref的情景:

  1. 管理焦点,文本选择或媒体播放。
  2. 触发强制动画。
  3. 集成第三方 DOM 库。

在组件中操作render中的DOM元素

官方的例子举的是管理焦点,我来自己写个例子看看。

写一个input框和一个按钮,当按钮点击的时候,input框获得焦点,input框和按钮在同一个组件里

class InputAndButton extends Component {

    constructor(props, context) {
        super(props, context);
        this.inputRef = React.createRef();
    }

    inputFocus = ()=>{
        this.inputRef.current.focus();
    }

    render() {
        return (
            <div>
                <input type="text" ref={this.inputRef}/>
                <button onClick={this.inputFocus}>聚焦</button>
            </div>
        );
    }
}

export default InputAndButton;

这里渲染了两个元素,如果不使用ref,会发现button和input是同级的DOM元素,很难用button来控制input。

这里看代码的话,是先使用createRef创建一个类的属性,然后在元素上使用特殊的ref={}来指定这个属性。看似是把inputRef赋给ref,实际上是把ref属性所在的元素,赋值给inputRef.current属性,在类中的其他地方,都可以使用inputRef.current来访问这个DOM。

这么写之后,按button,就可以控制input,这是比较方便的地方。

操作其他组件

其实可以把ref想象成一个标记,这个标记最后落到哪个组件或者DOM元素上,ref.current就指向那个东西。

看上一个例子,从类的角度来说,inputFocus是InputAndButton组件的一个方法,可以调用这个方法。

把ref标记在组件上的时候,ref.current就指向那个组件,由于组件是类,所以可以调用组件的方法。

这里我们另外再写一个组件,在其中把ref放到InputAndButton上去:

import React, {Component} from 'react';
import InputAndButton from "./InputAndButton";

class AComponent extends Component {

    constructor(props, context) {
        super(props, context);

        this.sanerRef = React.createRef();
    }

    handleClick = ()=>{
        this.sanerRef.current.inputFocus();
    }

    render() {
        return (
            <div>
                <InputAndButton ref={this.sanerRef}/>
                <button onClick={this.handleClick}>另外一个按钮也能获取焦点</button>
            </div>
        );
    }
}

export default AComponent;

在我们的新组件中,有另外一个按钮,在这个组件中,又定义了一个sanerRef标记,然后把这个标记打到InputAndButton组件上,此时sanerRef.current就指向了InputAndButton组件,在handleClick中调用了InputAndButton类的方法inputFocus,结果就是input框获取了焦点。

注意此种方法,ref标记必须打在类组件上,不能是函数组件。

ref转发

这个意思就是说这个标记还可以被一层层的转发下去,从而让最上层的组件的标记,也能打到自己想要的深度,打到自己想要的元素或者组件上去。

ref转发需要使用函数式组件的写法,并且利用React库里一个特殊的方法,编写一个组件如下:

import React from 'react';

const MyInput = React.forwardRef(
    ((props, ref) => (<input type="text" ref={ref}/>))
);

export default MyInput;

这个组件的意思是,当外界有一个ref标记打到这个组件上的时候,这个组件会接力把标记打到input框上,所以最后那个标记实际上是打到了input框上。

把这个组件和其他组件结合起来使用:

import MyInput from "./MyInput";

class AComponent extends Component {

    constructor(props, context) {
        super(props, context);

        this.sanerRef = React.createRef();
    }

    handleClick  = ()=>{
        console.log(this.sanerRef.current);
        this.sanerRef.current.focus();
    }


    render() {
        return (
            <div>
                <MyInput ref={this.sanerRef}/>
                <button onClick={this.handleClick}>另外一个按钮也能获取焦点</button>
            </div>
        );
    }
}

sanerRef的标记打在了MyInput组件上,此时sanerRef.current就是MyInput组件吗?不是的,这个标记被MyInput继续转发,最终打在了其中的input标签上,在控制台打印出的this.sanerRef.current是DOM元素,所以对其调用了原生的DOM API focus()来获取焦点。

好,对于ref,目前知道这么多基本足够了。

LICENSED UNDER CC BY-NC-SA 4.0
Comment