基于本文回答

播面 播面

刷题像听歌,多听自然懂
0
评论

React 中,useRef 和forwardRef是什么

知识点图片

在 React 中,useRefforwardRef 都与 Ref(引用) 有关,但它们的作用和使用场景完全不同。

简单来说:

  • useRef 是用来 “创建” 引用的(或者存储跨渲染的数据)。
  • forwardRef 是用来 “传递” 引用的(让父组件可以操作子组件内部的 DOM)。

下面详细解释这两个概念。


1. useRef

useRef 是一个 React Hook。

作用

它返回一个可变的 ref 对象,该对象只有一个 current 属性。
const myRef = useRef(initialValue);

核心特性

  1. 访问 DOM 节点:这是最常用的功能。你可以直接获取 HTML 元素的底层 DOM 节点(例如让输入框聚焦、滚动到特定位置)。
  2. 存储变量(不触发重渲染):它就像一个“盒子”,你可以往 current 里存任何数据。修改 ref.current 不会触发组件重新渲染。这与 useState 不同(useState 改变会触发重渲染)。
  3. 生命周期保持:在组件的整个生命周期内,返回的同一个 ref 对象保持不变。

代码示例:访问 DOM

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

function TextInput() {
  // 1. 创建 ref
  const inputEl = useRef(null);

  const onButtonClick = () => {
    // 3. 通过 .current 访问原生 DOM API
    inputEl.current.focus(); 
  };

  return (
    <>
      {/* 2. 绑定 ref 到元素 */}
      <input ref={inputEl} type="text" />
      <button onClick={onButtonClick}>聚焦输入框</button>
    </>
  );
}

2. forwardRef

forwardRef 是一个高阶组件(HOC)或 API 函数。

作用

它允许父组件将 ref “转发” (forward) 给子组件内部的某个 DOM 元素。

为什么需要它?

默认情况下,函数组件不能接收 ref 属性。如果你尝试在一个自定义组件上使用 ref(例如 <MyComponent ref={ref} />),React 会报错或忽略它,因为函数组件没有实例。

如果你希望父组件能够获取子组件内部某个 HTML 标签的引用,就需要用 forwardRef 包裹子组件。

代码示例:父组件控制子组件的 Input

jsx
import React, { useRef, forwardRef } from 'react';

// 子组件:使用 forwardRef 包裹
// props 是父组件传的参数,ref 是父组件传下来的引用
const MyInput = forwardRef((props, ref) => {
  return (
    <input 
      ref={ref} // 将父组件的 ref 绑定到内部的 input 
      className="fancy-input" 
      {...props} 
    />
  );
});

// 父组件
function Parent() {
  const inputRef = useRef(null); // 父组件创建 ref

  const handleClick = () => {
    // 父组件可以直接操作子组件内部的 input DOM
    inputRef.current.focus();
    inputRef.current.value = "Hello World";
  };

  return (
    <div>
      {/* 将 ref 传递给自定义组件 */}
      <MyInput ref={inputRef} placeholder="我是子组件" />
      <button onClick={handleClick}>父组件控制子组件聚焦</button>
    </div>
  );
}

3. 它们如何配合使用?

通常的模式是:

  1. 父组件 使用 useRef 创建一个 ref 对象。
  2. 父组件 将这个 ref 传递给 子组件
  3. 子组件 使用 forwardRef 接收这个 ref,并将其绑定到自己的某个 HTML 元素上。

进阶:搭配 useImperativeHandle

有时候,你不想把子组件原本的 DOM 节点完全暴露给父组件(为了安全或封装性),只想暴露几个特定的方法(比如 focus, reset)。这时会用到 useImperativeHandle 配合 forwardRef

jsx
const MyInput = forwardRef((props, ref) => {
  const inputRef = useRef();

  // 定义暴露给父组件的方法
  useImperativeHandle(ref, () => ({
    focus: () => {
      inputRef.current.focus();
    },
    alertValue: () => {
      alert(inputRef.current.value);
    }
  }));

  return <input ref={inputRef} />;
});

总结对比

特性 useRef forwardRef
类型 Hook (钩子) API / 高阶函数
主要目的 创建引用容器,或存储跨渲染数据 传递引用,穿透组件层级
返回值 { current: ... } 对象 一个 React 组件
触发渲染 修改时不触发渲染 本身不涉及渲染逻辑
使用位置 组件内部逻辑中 定义组件时包裹组件函数
一句话概括 "我要存个东西/我要抓个DOM" "我要把父组件给我的Ref转交给我的子元素"
00:00
00:00