You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React中ForwardedRef与RefObject的区别及适配问题

React ForwardedRef 与 RefObject 类型不匹配问题解析

问题代码示例

export const ScrollUtilsProvider = React.forwardRef<HTMLDivElement, ScrollUtilsProviderProps>(
  (props, ref) => {
    const scrollUtils = useScrollUtils(ref) // 此处出现报错
    return <div ref={ref}><ScrollUtilsContext.Provider value={scrollUtils}>{props.children}</ScrollUtilsContext.Provider></div>
  },
)

export const useScrollUtils = <T extends Element>(ref: RefObject<T>) => {
  return {
    // 基于传入ref实现的功能函数
  }
}

报错信息

Argument of type 'ForwardedRef<HTMLDivElement>' is not assignable to parameter of type 'RefObject<HTMLDivElement>'.
  Type 'null' is not assignable to type 'RefObject<HTMLDivElement>'.

类型定义对比

// 来自@types/react@17.0.0
interface RefObject<T> {
  readonly current: T | null;
}
type ForwardedRef<T> = ((instance: T | null) => void) | MutableRefObject<T | null> | null;

问题解答

1. 为何这两种类型差异如此之大?

RefObject 是 React 通过 useRef 或 createRef 创建的固定结构对象,核心是只读的 current 属性,专门用于存储 DOM 元素或组件实例的引用,属于静态的引用存储容器。

而 ForwardedRef 是为 forwardRef 量身设计的类型,它需要兼容父组件传递 ref 的三种常见场景:

  • 回调函数式 ref(比如 (el) => { ... })
  • MutableRefObject(useRef 创建的可修改 ref 对象)
  • 未传入任何 ref(即 null)

这种设计是为了让 forwardRef 能灵活接收父组件传递的各种 ref 形式,所以类型范围比 RefObject 宽泛得多。

2. 是否可以将 ForwardedRef 当作 RefObject 使用?

直接使用不行,因为 ForwardedRef 可能是函数或 null,不符合 RefObject 的固定结构要求。但可以通过转换逻辑适配:

你可以创建一个内部的 RefObject,再同步这个内部 ref 和 ForwardedRef 的更新状态,示例代码如下:

export const ScrollUtilsProvider = React.forwardRef<HTMLDivElement, ScrollUtilsProviderProps>(
  (props, ref) => {
    const internalRef = useRef<HTMLDivElement>(null);
    const scrollUtils = useScrollUtils(internalRef);

    // 同步内部ref与转发ref的状态
    useEffect(() => {
      if (typeof ref === 'function') {
        ref(internalRef.current);
      } else if (ref != null) {
        ref.current = internalRef.current;
      }
    }, [ref, internalRef.current]);

    return (
      <div ref={internalRef}>
        <ScrollUtilsContext.Provider value={scrollUtils}>
          {props.children}
        </ScrollUtilsContext.Provider>
      </div>
    );
  },
);

这样既满足了 useScrollUtils 对 RefObject 的参数要求,又能正确将内部 DOM 引用转发给父组件传入的 ForwardedRef。

3. ForwardedRef 有何特殊之处?

ForwardedRef 的特殊之处在于它是多态的 ref 传递管道,而非存储引用的容器:

  • 它允许父组件以任意形式(回调、对象、null)传递 ref
  • 核心作用是让子组件能把内部 DOM 或组件实例的引用暴露给父组件
  • 本身不持有引用,仅负责将引用传递给父组件指定的接收方式

而 RefObject 是单一职责的引用存储容器,只能用来保存引用,无法直接作为 forwardRef 的参数类型使用。

内容的提问来源于stack exchange,提问作者Yehoshua Oliveira

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 00:27:08