useRef、ref 和 forwardRef 的区别

React 中三种 ref 相关 API 的使用场景和区别

问题

useRef、ref 和 forwardRef 这三个 API 有什么区别?分别在什么场景下使用?

解答

useRef

useRef 是一个 Hook 函数,用于函数组件中创建可变的 ref 对象。返回的 ref 对象在组件整个生命周期内保持不变,不会因为重新渲染而改变。

function MyComponent() {
  const inputRef = useRef(null);
  
  const focusInput = () => {
    inputRef.current.focus();
  };
  
  return (
    <>
      <input ref={inputRef} />
      <button onClick={focusInput}>聚焦输入框</button>
    </>
  );
}

useRef 主要用于:

  • 存储 DOM 元素引用
  • 保存任何可变值(不会触发重新渲染)
  • 替代类组件中的实例变量

ref

ref 是 React 元素的一个属性,可以在类组件和函数组件中使用。在类组件中,ref 可以直接访问组件实例。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }
  
  focusInput = () => {
    this.inputRef.current.focus();
  };
  
  render() {
    return (
      <>
        <input ref={this.inputRef} />
        <button onClick={this.focusInput}>聚焦输入框</button>
      </>
    );
  }
}

forwardRef

forwardRef 用于将父组件的 ref 转发到子组件内部的 DOM 元素或组件实例。这在封装可复用组件时特别有用。

const FancyInput = React.forwardRef((props, ref) => {
  return <input ref={ref} className="fancy-input" {...props} />;
});

function Parent() {
  const inputRef = useRef(null);
  
  return <FancyInput ref={inputRef} />;
}

关键点

  • useRef 是 Hook,用于函数组件;createRef 用于类组件,每次渲染都会创建新实例
  • useRef 返回的对象在整个生命周期内保持不变,适合存储不触发渲染的可变值
  • forwardRef 用于将 ref 从父组件传递到子组件内部,解决函数组件无法直接接收 ref 的问题
  • ref 本身只是一个属性,可以指向 DOM 元素、类组件实例或 useRef/createRef 创建的对象