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

React forwardRef组件未接收父组件ref时,三种内部创建ref方案如何选择

方案对比与最优选择

三个方案优劣分析

  • Option 1:const divRef = ref || React.createRef();
    存在严重缺陷,不可用。React.createRef()每次调用都会生成全新的ref对象,而forwardRef的渲染函数在组件每一次重渲染时都会执行。如果父组件未传递ref,每次重渲染都会生成新的divRef,导致之前存储的DOM引用丢失,内部逻辑依赖ref的话会出现不可预期的错误。
  • Option 2:
    const _ref = useRef(null);
    const divRef = ref || _ref;
    
    是三种方案里的最优解。useRef创建的ref对象在组件整个生命周期内唯一,不会随重渲染变化。父组件传递ref时直接使用父级ref,父级可以正常拿到DOM引用;未传递时使用内部稳定的ref对象,完全满足需求。唯一需要注意的是该写法仅支持父组件传递对象类型ref的场景,如果父组件传递的是函数类型ref,需要额外适配。
  • Option 3:const divRef = useRef(ref?.current);
    存在逻辑缺陷,不满足需求。useRef的初始值仅在组件第一次初始化时生效,且该写法无论父组件是否传递ref,最终使用的都是组件内部创建的新ref对象,完全忽略了父级传入的ref本身。父组件传递ref时无法拿到对应的DOM引用,只有父组件未传ref的场景下看起来能正常运行。

Option2与Option3是否等价

二者完全不等价,核心差异在于对父级传入ref的处理:

  • Option2在父级传ref时会直接绑定父级的ref对象,父级可正常获取DOM引用
  • Option3全程使用内部创建的ref,父级传入的ref会被完全忽略,仅在父级未传ref时表现和Option2一致

更完善的通用实现

如果需要兼容父级传递函数类型ref的场景,可以使用回调ref的写法统一处理:

export const Child = React.forwardRef((props, parentRef) => {
  const innerRef = useRef(null);

  const divRef = useCallback((node) => {
    // 同时赋值内部ref和父级ref
    innerRef.current = node;
    if (typeof parentRef === 'function') {
      parentRef(node);
    } else if (parentRef != null) {
      parentRef.current = node;
    }
  }, [parentRef]);

  // 内部使用innerRef即可拿到DOM引用
  useEffect(() => {
    console.log("divRef: ", innerRef.current);
  }, []);

  return <div ref={divRef}>child</div>;
});

内容的提问来源于stack exchange,提问作者Lin Du

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 04:00:03