React 中的 `useRef`、`ref` 和 `forwardRef`:区别与使用场景

643 阅读3分钟

基本概念

在 React 开发中,useRefrefforwardRef 是三个与引用(reference)相关的核心概念。它们虽然都与 DOM 操作或组件实例的引用有关,但各自的使用场景和功能有所不同。本文将详细探讨它们的区别,并通过示例帮助你更好地理解它们的应用。

1. ref:React 中的引用

ref 是 React 提供的一种机制,用于直接访问 DOM 元素或组件实例。通常情况下,React 推荐使用声明式的数据流来管理组件状态,但在某些场景下,直接操作 DOM 是不可避免的。这时,ref 就派上了用场。

使用场景

  • 访问 DOM 元素(如输入框、按钮等)。
  • 获取子组件的实例(仅限于类组件)。

示例

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

  componentDidMount() {
    // 访问 DOM 元素
    this.myRef.current.focus();
  }

  render() {
    return <input type="text" ref={this.myRef} />;
  }
}

在上面的例子中,React.createRef() 创建了一个 ref,并将其附加到 input 元素上。通过 this.myRef.current,我们可以直接访问该 DOM 元素。

2. useRef:函数组件中的引用

useRef 是 React Hooks 提供的一个 API,用于在函数组件中创建引用。与 React.createRef() 类似,useRef 也可以用来访问 DOM 元素或存储可变值。不同的是,useRef 在函数组件的每次渲染中都会返回同一个引用对象。

使用场景

  • 访问 DOM 元素。
  • 存储可变值(类似于类组件中的实例变量)。

示例

function MyComponent() {
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    // 访问 DOM 元素
    inputRef.current.focus();
  }, []);

  return <input type="text" ref={inputRef} />;
}

在这个例子中,useRef 创建了一个引用,并将其附加到 input 元素上。与类组件中的 ref 类似,inputRef.current 可以访问 DOM 元素。

存储可变值

useRef 还可以用来存储可变值,这些值在组件的生命周期中保持不变,且不会触发重新渲染。

function Timer() {
  const timerRef = React.useRef(0);

  const startTimer = () => {
    timerRef.current = setInterval(() => {
      console.log("Timer is running...");
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(timerRef.current);
  };

  return (
    <div>
      <button onClick={startTimer}>Start Timer</button>
      <button onClick={stopTimer}>Stop Timer</button>
    </div>
  );
}

在这个例子中,timerRef 用于存储定时器的 ID,即使组件重新渲染,timerRef.current 的值也不会丢失。

3. forwardRef:转发引用

forwardRef 是 React 提供的一个高阶函数,用于将 ref 从父组件转发到子组件。这在某些场景下非常有用,例如当你需要在父组件中直接访问子组件的 DOM 元素或实例时。

使用场景

  • ref 传递给子组件。
  • 在父组件中访问子组件的 DOM 元素或实例。

示例

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

function ParentComponent() {
  const inputRef = React.useRef(null);

  React.useEffect(() => {
    // 访问子组件的 DOM 元素
    inputRef.current.focus();
  }, []);

  return <ChildComponent ref={inputRef} />;
}

在这个例子中,ChildComponent 使用 forwardRefref 转发到 input 元素上。父组件 ParentComponent 可以通过 inputRef 直接访问子组件的 input 元素。

4. 三者的区别

特性refuseRefforwardRef
使用场景类组件中访问 DOM 或组件实例函数组件中访问 DOM 或存储可变值ref 从父组件转发到子组件
创建方式React.createRef()React.useRef()React.forwardRef()
适用组件类组件函数组件函数组件或类组件
存储值仅用于 DOM 或组件实例可用于 DOM 或存储任意可变值用于转发 ref

5. 总结

  • ref:主要用于类组件中,用于访问 DOM 元素或子组件实例。
  • useRef:用于函数组件中,既可以访问 DOM 元素,也可以存储可变值。
  • forwardRef:用于将 ref 从父组件转发到子组件,适用于需要跨组件访问 DOM 或实例的场景。