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

React中如何以类型安全方式访问forwardRef的ref.current属性

问题原因

forwardRef注入的第二个参数parentRef类型为React.ForwardedRef<T>,是三分支联合类型:

  • 带current属性的ref对象:React.RefObject<T>
  • 无current属性的回调ref:(instance: T | null) => void
  • 父组件未传ref时的空值:null

原代码通过parentRef ?? localRef合并ref时,得到的canvasRef类型会保留回调ref的函数分支,TypeScript无法确定该值一定存在current属性,因此会阻止直接访问.current,抛出类型错误。

类型安全实现方案

禁止直接合并ref使用,以下两种写法完全符合React类型定义,可实现安全访问:

方案1:使用官方API useImperativeHandle(优先推荐)

这是React官方提供的标准ref转发API,内部已自动兼容父ref的所有形态(对象/回调/空值),本地维护的ref始终是类型明确的RefObject,访问.current无类型问题:

import { forwardRef, useRef, useEffect, useImperativeHandle } from 'react';

const CanvasContainer = forwardRef<HTMLCanvasElement>((props, parentRef) => {
  const localRef = useRef<HTMLCanvasElement>(null);

  // 将本地ref的值同步给父组件传入的ref
  useImperativeHandle(parentRef, () => localRef.current as HTMLCanvasElement, []);

  useEffect(() => {
    console.log(localRef.current);
  }, []);

  return <canvas ref={localRef} />;
});

方案2:自定义ref回调手动同步

如果不使用useImperativeHandle,可以自定义ref回调函数,手动同步ref值到本地ref和父ref,同样保证类型安全:

import { forwardRef, useRef, useEffect, useCallback } from 'react';

const CanvasContainer = forwardRef<HTMLCanvasElement>((props, parentRef) => {
  const localRef = useRef<HTMLCanvasElement>(null);

  const setCanvasRef = useCallback((node: HTMLCanvasElement | null) => {
    // 更新本地ref
    localRef.current = node;
    // 同步值给父ref,兼容所有ref形态
    if (typeof parentRef === 'function') {
      parentRef(node);
    } else if (parentRef) {
      parentRef.current = node;
    }
  }, [parentRef]);

  useEffect(() => {
    console.log(localRef.current);
  }, []);

  return <canvas ref={setCanvasRef} />;
});

注意:不要通过类型强转(如as React.RefObject<HTMLCanvasElement>)绕过TS校验,该方式无法处理父组件传入回调ref的场景,会引发运行时错误。

内容的提问来源于stack exchange,提问作者Vlad Nicula

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 01:15:32