为何需使用React.forwardRef?直接用ComponentPropsWithRef的疑问
先看你给出的代码:
type TextboxProps = React.ComponentPropsWithRef<'input'>; const Textbox = (props: TextboxProps): ReactElement | null => { return ( <input type='number' value={props.value} onChange={props.onChange} ref={props.ref} /> ); }; export { Textbox };
这种写法存在几个关键问题:
1. 运行时ref传递失效
React中ref是特殊属性,不属于props范畴。当你给自定义组件传递ref时,React运行时不会把它放到props对象里,而是单独处理。你的代码里虽然用ComponentPropsWithRef定义了props包含ref类型,但实际运行时,外部传入的ref根本不会出现在props.ref中,导致内部<input>的ref绑定完全无效——外部获取到的ref始终是null。
比如你这么用:
const inputRef = useRef<HTMLInputElement>(null); <Textbox ref={inputRef} />
TypeScript不会报错,但inputRef.current永远拿不到实际的input DOM元素。
2. 类型安全的假象
ComponentPropsWithRef<'input'>确实帮你把ref的类型加到了props里,但这只是静态类型层面的提示,无法改变React运行时的规则。你以为组件能接收ref,但实际上根本接不到,属于“类型通过但功能失效”的陷阱。
3. 无法适配复杂组件场景
如果后续你的组件需要封装多层结构,或者要对ref做额外处理(比如合并多个ref、转发ref到内部非直接根元素),不用forwardRef的话,你根本拿不到外部传入的ref对象,完全没法实现这类需求。
正确的写法应该是这样
用React.forwardRef接收外部传入的ref,再转发给内部元素:
// 用ComponentPropsWithoutRef排除ref,因为ref会通过forwardRef的第二个参数接收 type TextboxProps = React.ComponentPropsWithoutRef<'input'>; const Textbox = React.forwardRef<HTMLInputElement, TextboxProps>((props, ref) => { return ( <input type='number' {...props} ref={ref} /> ); }); export { Textbox };
或者如果你坚持用ComponentPropsWithRef,也需要结合forwardRef,此时ComponentPropsWithRef的ref类型会和forwardRef的泛型对应:
type TextboxProps = React.ComponentPropsWithRef<'input'>; const Textbox = React.forwardRef<HTMLInputElement, Omit<TextboxProps, 'ref'>>((props, ref) => { return ( <input type='number' {...props} ref={ref} /> ); });
简单说,ComponentPropsWithRef只是帮你生成包含ref类型的props定义,但必须配合React.forwardRef才能让ref在运行时正确传递,这是React的底层机制决定的。
内容的提问来源于stack exchange,提问作者tryonix

