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
相关产品推荐
相关产品推荐

