React 的几种 Ref 的使用介绍

5105次浏览

前言

2023年初的计划都实现了吗?呵呵,不管怎么样,迄今为止我博客坚持时间也马上10年了,2014年,我创建了本博客,如今马上2024年了,本博客也马上10年了,10年见证了互联网的兴起,鼎盛。其实坚持做一件事不容易,希望大家多多支持。本文主要总结一下React 的几种 Ref 的使用,之前也有文章介绍过react父组件调用子组件的方法小结,其实这里介绍了一些ref的用法,但是不是很全面,本文再次总结一下,作为2023的尾声!

ref的三种调用方式

一. String Refs

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    componentDidMount() {
        setTimeout(() => {
             // 2. 通过 this.refs.xxx 获取 DOM 节点
             this.refs.textInput.value = 'new value'
        }, 2000)
    }
    render() {
        // 1. ref 直接传入一个字符串
        return (
            <div>
              <input ref="textInput" value='value' />
            </div>
        )
    }
}

root.render(<App />);

二、回调 Refs

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    componentDidMount() {
        setTimeout(() => {
              // 2. 通过实例属性获取 DOM 节点
              this.textInput.value = 'new value'
        }, 2000)
    }
    render() {
        // 1. ref 传入一个回调函数
        // 该函数中接受 React 组件实例或 DOM 元素作为参数
        // 我们通常会将其存储到具体的实例属性(this.textInput)
        return (
            <div>
              <input ref={(element) => {
                this.textInput = element;
              }} value='value' />
            </div>
        )
    }
}

root.render(<App />);

三、 createRef

class App extends React.Component {
    constructor(props) {
        super(props)
        // 1. 使用 createRef 创建 Refs
        // 并将 Refs 分配给实例属性 textInputRef,以便在整个组件中引用
        this.textInputRef = React.createRef();
    }
    componentDidMount() {
        setTimeout(() => {
            // 3. 通过 Refs 的 current 属性进行引用
            this.textInputRef.current.value = 'new value'
        }, 2000)
    }
    render() {
        // 2. 通过 ref 属性附加到 React 元素
        return (
            <div>
              <input ref={this.textInputRef} value='value' />
            </div>
        )
    }
}

这是最被推荐使用的方式。

ref可以起到调用子级组件的作用

这个我开头说了,可以用作父组件调用子组件函数来使用。

例如如下一个例子:

class Input extends React.Component {
    constructor(props) {
        super(props)
        this.textInputRef = React.createRef();
    }
    handleFocus() {
        this.textInputRef.current.focus();
    }
    render() {
        return <input ref={this.textInputRef} value='value' />
    }
}

class App extends React.Component {
    constructor(props) {
        super(props)
        this.inputRef = React.createRef();
    }
    componentDidMount() {
        setTimeout(() => {
                this.inputRef.current.handleFocus()
        }, 2000)
    }
    render() {
        return (
            <div>
              <Input ref={this.inputRef} value='value' />
            </div>
        )
    }
}

具体父子组件hooks或者component 可以看我之前文章https://www.haorooms.com/post/react_class_hooks_dy

forwardRef

上面例子中,假如input我们用hooks实现,那么就会报错,如何input用hooks呢? React 提供了 forwardRef 这个 API,我们直接看使用示例:

// 3. 子组件通过 forwardRef 获取 ref,并通过 ref 属性绑定 React 元素
const Child = forwardRef((props, ref) => (
  <input ref={ref} placeholder="value" />
));

class Parent extends React.Component {
    constructor(props) {
        super(props)
        // 1. 创建 refs
        this.inputRef = React.createRef();
    }
    componentDidMount() {
        setTimeout(() => {
            // 4. 使用 this.inputRef.current 获取子组件中渲染的 DOM 节点
            this.inputRef.current.value = 'new value'
        }, 2000)
    }
    render() {
        // 2. 传给子组件的 ref 属性
        return <Child ref={this.inputRef} />
    }
}

这样就可以了

相关源码解析

现在我们看下 createRef 的源码,源码的位置在 /packages/react/src/ReactCreateRef.js,代码其实很简单,就只是返回了一个具有 current 属性的对象:

// 简化后
export function createRef() {
  const refObject = {
    current: null,
  };
  return refObject;
}

在渲染的过程中,refObject.current 会被赋予具体的值。

那 forwardRef 源码呢?源码的位置在 /packages/react/src/ReactForwardRef.js,代码也很简单:

// 简化后
const REACT_FORWARD_REF_TYPE = Symbol.for('react.forward_ref');

export function forwardRef(render) {
  const elementType = {
    $$typeof: REACT_FORWARD_REF_TYPE,
    render,
  };

  return elementType;
}

但是要注意这里的 $$typeof,尽管这里是 REACT_FORWARD_REF_TYPE,但最终创建的 React 元素的 $$typeof 依然为 REACT_ELEMENT_TYPE。

小结

2023年最后一篇文章,祝大家新年快乐,2024心想事成吧! 今天就先到这里。

Tags: refreact

相关文章: