React Ref 丢失?你需要 forwardRef

224 阅读2分钟

在 React 开发中,useRefforwardRef 是两个重要的工具,特别是在我们需要直接操作 DOM 或者需要将父组件中的 ref 转发给子组件内部的 DOM 元素时。这篇文章将详细介绍如何使用 useRefforwardRef 来解决常见的 Ref 丢失问题,并通过实际代码示例帮助我们更好地理解和应用这些概念。


一、什么是 useRef

✅ 定义

useRef 是一个 React Hook,它允许你创建一个引用对象(ref),这个对象在整个组件生命周期中保持不变。你可以用它来保存对 DOM 元素或组件实例的引用,或者存储任何可变值。

📦 语法

const ref = useRef(initialValue);
  • initialValue:初始值,通常为 null(用于绑定 DOM)或其他基础类型。

🧪 示例:自动聚焦输入框

import React, { useRef, useEffect } from 'react';

function App() {
  const inputRef = useRef(null);

  useEffect(() => {
    // 组件挂载后聚焦输入框
    inputRef.current.focus();
  }, []);

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

在这个例子中:

  • inputRef 是一个引用对象,指向 <input> 元素。
  • inputRef.current 访问到的就是 <input> 元素本身,可以调用其方法如 .focus()

二、为什么需要 forwardRef

❗问题来了

如果把 input 封装成一个自定义组件:

function Guang() {
  return <input type="text" />;
}

然后在父组件中尝试控制它:

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

就会发现:inputRef.currentnull拿不到 input 元素!

❗为什么会这样?

因为 React 的组件默认不会自动转发 ref。也就是说:

父组件传来的 ref,并不会自动绑定到子组件里的 DOM 元素上。


三、什么是 forwardRef

forwardRef 的作用

forwardRef 是 React 提供的一个工具,用来把父组件传来的 ref 转发给子组件内部的 DOM 元素

✅ 举个例子

假设我们有一个自定义组件 Guang,并希望在父组件中通过 ref 控制它的内部 DOM 元素:

function Guang(props, ref) {
  return (
    <div>
      <input type="text" ref={ref} />
    </div>
  );
}

const WrapperGuang = forwardRef(Guang);//把 `Guang` 包装成一个可以接收 `ref` 的组件

  • Guang 接收 propsref
  • ref 传给了 input,这样父组件就能通过 ref 访问到这个 input

现在我们就可以在父组件中控制这个 input:

function App() {
  const inputRef = useRef(null);

  useEffect(() => {
    inputRef.current?.focus(); // ✅ 可以聚焦了!
  }, []);

  return (
    <div className='App'>
      <WrapperGuang ref={inputRef} />
    </div>
  );
}
  • 组件挂载后,input 自动聚焦

forwardRef 就像是一个“信号中继站”,把遥控器的信号(ref)传给屋子里的电视(DOM 元素)。


四、常见使用场景

✅ 场景一:自动聚焦输入框

useEffect(() => {
  inputRef.current.focus();
}, [])

✅ 场景二:滚动到某个元素

inputRef.current.scrollIntoView({ behavior: 'smooth' });

✅ 场景三:控制视频播放

const videoRef = useRef(null);
videoRef.current.play();

✅ 场景四:封装可聚焦的组件

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