「React基本功」refs详解

143 阅读3分钟

在 React 中,refs 是用于直接访问 DOM 元素或子组件实例的工具。它提供了一种在某些情况下访问底层 DOM 或获取组件实例的方式,而不依赖于组件的声明式数据流。refs 的使用应当保持谨慎,因为它违背了 React 的核心理念——通过 props 和 state 进行数据传递与更新。

1. 创建 ref

在 React 中,可以通过 React.createRef() 方法创建一个 ref,然后将它附加到组件的某个元素上。

示例:
import React, { Component } from 'react';

class MyComponent extends Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef(); // 创建一个 ref
  }

  componentDidMount() {
    // 通过 ref 访问 DOM 元素
    console.log(this.myRef.current); 
  }

  render() {
    return <div ref={this.myRef}>Hello, world!</div>;
  }
}

在这个例子中,this.myRef 被附加到 div 元素上,React 会通过 this.myRef.current 返回该 DOM 元素的引用。

2. 使用函数式组件中的 ref

在 React 的函数式组件中,使用 useRef Hook 来创建 ref

示例:
import React, { useRef, useEffect } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  useEffect(() => {
    // 访问 DOM 元素
    console.log(myRef.current);
  }, []);

  return <div ref={myRef}>Hello, React!</div>;
}

在这个例子中,useRefcreateRef 类似,可以直接访问 div 元素。

3. ref 的常见应用

3.1 操作 DOM

有时我们需要直接操作 DOM 元素,通常是在第三方库需要访问底层 DOM 的时候,比如 focusscroll 或调用 HTML5 API 等。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  focusInput = () => {
    this.inputRef.current.focus(); // 使用 ref 操作 DOM
  }

  render() {
    return (
      <div>
        <input ref={this.inputRef} />
        <button onClick={this.focusInput}>Focus Input</button>
      </div>
    );
  }
}
3.2 访问子组件的实例

refs 还可以用来获取类组件的实例,这在你需要直接调用子组件实例上的方法时很有用。

class ChildComponent extends React.Component {
  sayHello() {
    console.log('Hello from ChildComponent');
  }

  render() {
    return <div>I'm a child component</div>;
  }
}

class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.childRef = React.createRef();
  }

  handleClick = () => {
    this.childRef.current.sayHello(); // 访问子组件实例
  }

  render() {
    return (
      <div>
        <ChildComponent ref={this.childRef} />
        <button onClick={this.handleClick}>Call Child Method</button>
      </div>
    );
  }
}

4. refs 的使用场景

  • 访问 DOM 元素:在某些需要直接操作 DOM 的情况下(例如,手动设置焦点、媒体播放控制、集成第三方库等)。
  • 触发动画:你可能需要通过 ref 来直接操作 DOM 以实现某些动画效果。
  • 第三方库集成:在集成一些需要访问原始 DOM 元素的库时,ref 是一个很好的方式。

5. 避免过度使用 refs

尽量减少对 refs 的依赖,因为它通常表示你的设计中存在某种不符合 React 哲学的数据流方式。React 的主要特点是数据驱动的 UI 渲染,refs 则打破了这种声明式的流程,因此只在必要的情况下使用它们。

6. forwardRef 的使用

如果你需要在父组件中访问函数式子组件的 ref,可以使用 React.forwardRef

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

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

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

  render() {
    return (
      <div>
        <MyInput ref={this.inputRef} />
        <button onClick={this.handleFocus}>Focus Input</button>
      </div>
    );
  }
}

forwardRef 允许父组件获取到子组件中的 ref,从而可以访问子组件中的 DOM 元素。

总结

  • refs 用于直接访问 DOM 元素或组件实例,适用于需要直接操作 DOM 的场景。
  • 函数组件中可以使用 useRef 来创建 ref
  • forwardRef 允许将 ref 传递给子组件。

在日常开发中,尽量避免过度使用 refs,应当优先使用 React 的数据流和组件间通信机制来处理问题。