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

React中将ref存入父组件state是否合理?跨组件获取DOM方案

结论先行

把DOM引用存储在父组件state中本身不是不良实践,属于场景化的可行方案,但你当前的业务场景有更稳妥、更符合React设计逻辑的实现方式,不需要自己手动维护DOM到state的赋值逻辑。

你遇到的核心问题本质是:普通useRef创建的ref对象值变更不会触发组件重渲染,因此动态挂载的ComponentA完成核心DOM挂载后,父组件无法自动感知变化、触发ComponentB的重渲染,这是普通ref的特性限制,和ref转发方案本身无关。

你提出的state存DOM方案的注意事项

这个方案可以正常运行,但有3个容易踩的坑:

  • 必须在ComponentA卸载时把对应state的值重置为null,否则ComponentB可能拿到已经被销毁的DOM引用,读取属性时直接报错,还会造成内存泄漏
  • 所有DOM属性的读写操作不要放在组件渲染阶段执行,要放到useLayoutEffect/useEffect里,避免触发反复的重排重绘
  • 不要对存DOM的state做深度比较,DOM是引用类型,浅比较就能判断变化,深度比较只会带来无意义的性能开销
更推荐的实现方案

方案1:回调ref(最轻量,完全匹配你的场景)

React原生支持的回调ref天然就能感知DOM的挂载和卸载时机,不需要你手动在子组件里调用父组件的setState传DOM,实现成本最低:

  1. 父组件WRAPPER中用state存储目标核心DOM,用useCallback缓存一个回调ref函数
  2. 通过ref转发把这个回调ref传给ComponentA,绑定到内部的核心DOM节点上
  3. DOM挂载时React会自动执行回调、传入DOM节点,DOM卸载时自动传入null,父组件的state会同步更新,自动触发ComponentB重渲染

示例代码:

function Wrapper() {
  const [coreDom, setCoreDom] = useState(null);
  // 缓存回调ref,避免子组件不必要的重渲染
  const bindCoreDom = useCallback((node) => {
    setCoreDom(node);
  }, []);

  return (
    <div>
      {shouldRenderA && <ComponentA ref={bindCoreDom} />}
      <ComponentB targetNode={coreDom} />
    </div>
  );
}

// ComponentA通过forwardRef把ref绑定到内部核心DOM
const ComponentA = forwardRef((props, ref) => {
  return (
    <div>
      {/* 其他业务元素 */}
      <div ref={ref} className="core-element">核心节点</div>
    </div>
  );
});

这个方案的可靠性比你手动在子组件生命周期里赋值state更高,React会保证回调ref的执行时机和DOM的挂载/卸载完全同步,不会出现时机错乱。

方案2:所需状态提升(耦合度更低)

如果ComponentB渲染只需要核心DOM的部分属性(比如尺寸、位置、滚动偏移量等),完全不需要把整个DOM引用传递给ComponentB:

  • 在WRAPPER层拿到核心DOM后,监听你需要的属性变化(可以配合ResizeObserver、滚动监听等)
  • 把需要的纯数据存在state中传递给ComponentB

这种方式下ComponentB只依赖纯数据渲染,不需要感知DOM的存在,组件耦合度更低,也能避免子组件随意操作DOM带来的不可控副作用。

不建议把DOM存在state的场景

如果你的场景符合以下情况,用普通useRef+ref转发即可,把DOM存在state里只会带来无意义的重渲染:

  • 拿到DOM引用只是为了调用DOM方法(比如触发聚焦、控制滚动、播放媒体等),不需要触发组件重渲染
  • 组件不存在动态挂载/卸载的逻辑,DOM和父组件生命周期完全同步

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 04:39:19