React Ref

11 5月

React 16开始ref的写法有了点变化,但作用不变。虽然ref并不是React的推荐写法,也破坏了数据驱动的思路,但不要试ref为洪水猛兽,优先解决问题。

  • 使用ref
  • 回调ref
  • 传递ref

使用ref

React.createRef()创建ref,使用ref中的current属性访问节点:

import React, { Component } from 'react';

class RefDemo extends Component {
    constructor(props) {
        super(props);
        this.textInput = React.createRef();     // 使用 React.createRef() 创建 refs
    }

    focusTextInput = () => {
        this.textInput.current.focus();     // 使用 ref 中的 current 属性进行访问
    };

    render() {
        // React 会在组件加载时将 DOM 传入 current 属性,在卸载时则会改回 null
        // ref 的更新会发生在 componentDidMount 或 componentDidUpdate 之前
        return (
            <div>
                <input type="text" ref={this.textInput} />
                <input type="button" value="聚焦输入框" onClick={this.focusTextInput} />
            </div>
        );
    }
}

也可以调用children的ref:

import React, { Component } from 'react';
import RefDemo from '../Ref';

class ParentRef extends Component {
    constructor(props) {
        super(props);
        this.textInput = React.createRef();
    }
    
    componentDidMount() {
        this.textInput.current.focusTextInput();
    }

    render() {
        return (
            <RefDemo ref={this.textInput} />
        );
    }
}

参照官网,ref对函数式组件的写法上有点约束,项目中多是类组件。当然类组件本身就是函数式组件的语法糖,当不需要生命周期钩子方法或state时,用函数式组件更高效,代码量更少。道理是这么说,but who care~,前端业务特性决定了,你今天写的代码,很快就会成为乐色。优先考虑开发效率,和规范团队的板式代码风格,尽量写类组件吧。

回调ref

React.createRef()创建的是ref对象,并不是真实的Dom节点的引用,所以你需要用current属性获取真实的Dom节点引用。

当然React16之前的回调ref(callback ref)的方式仍旧支持,回调ref的方式直接存储真实的Dom节点的引用:

import React, { Component } from 'react';

export default class CallbackRefDemo extends React.Component {
    constructor(props) {
        super(props);
        this.textInput = null;
    }

    setTextInputRef = (ele) => {       // 回调 ref
        this.textInput = ele;
    };

    focusTextInput = () => {
        if (this.textInput) {
            this.textInput.focus();     // 直接使用原生 API
        }
    };
  
    componentDidMount() {
        this.focusTextInput();
    }
  
    render() {
        // 使用 ref 的回调将 text 输入框的 DOM 节点存储到 React
        return (
            <div>
                <input type="text" ref={this.setTextInputRef} />
                <input type="button" value="Focus the text input" onClick={this.focusTextInput} />
            </div>
        );
    }
}

React16之前还有一种string类型的ref,这个可能会废弃,不推荐这样写了。

传递ref

可以使用传递ref方式(forwarding refs),在父组件中访问子组件中DOM,例如聚焦DOM元素,获取子组件中某DOM的位置等。

React.forwardRef((props, ref) => node)来创建子组件,通过参数ref可以指向具体的DOM节点。

父组件:

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

export default class FrowardingRef extends React.Component {
    constructor(props) {
        super(props);
        this.myRef = React.createRef();
    }
    
    componentDidMount() {
        this.myRef.current.focus();     // 操作子组件的 DOM
    }
    
    render() {
        return (
            <FancyButton ref={this.myRef}>子组件</FancyButton>
        );
    }
}

子组件:

import React from 'react';

// 用 React.forwardRef 创建子组件,参数为 props 和 ref,返回一个 React 组件
// 从父组件传递下来的 ref 绑定到具体 DOM 上
const FancyButton = React.forwardRef((props, ref) => (
    <button className="fancybutton" ref={ref}>
        {props.children}
    </button>
));

export default FancyButton;

另一种获取子组件的DOM的方法是,使用findDOMNode(),简单粗暴但是不推荐。

发表评论

电子邮件地址不会被公开。 必填项已用*标注