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

React中带包装组件时如何访问子组件的Ref?

问题描述

我正在学习React的forwardRef用法,基于官方文档完成的基础示例可以正常通过ref调用子组件的showAlert方法。但实际场景中需要为ChildComp添加多层包装(因遗留原因无法移除),包装后点击按钮时childCompRef.current显示为undefined。想了解Ref丢失的原因,以及如何让功能恢复正常,是否需要重新使用forwardRef?

基础示例代码

import { forwardRef, useRef, useImperativeHandle } from "react"

const ChildComp = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    showAlert() {
      alert("Hello from Child Component")
    },
  }))
  return <div></div>
})

function App() {
  const childCompRef = useRef()
  return (
    <div>
      <button onClick={() => childCompRef.current.showAlert()}>Click Me</button>
      <ChildComp ref={childCompRef} />
    </div>
  )
}

export default App

带包装的示例代码

import { Component, forwardRef, useRef, useImperativeHandle, React } from "react"

const getWrapper = () => {
  return class Test extends Component{
    public render() {
      return this.props.children;
    }
  }
}

const withWrapper = (
  Component: React.FunctionComponent | React.ForwardRefExoticComponent<React.RefAttributes<unknown>>
  ) => {
    function Wrapped(props: any) {
      return React.createElement(getWrapper(), {}, <Component {...props} />)
    }
    return Wrapped;
  }

const ChildComp = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    showAlert() {
      alert("Hello from Child Component!~")
    },
  }))
  return <div></div>
})

const WrappedChild = withWrapper(ChildComp);

function App() {
  const childCompRef = useRef()
  return (
    <div>
      <button onClick={() => childCompRef.current.showAlert()}>Click Me</button>
      <WrappedChild ref={childCompRef} />
    </div>
  )
}
原因分析
  • 当前使用的withWrapper高阶组件返回的是普通函数组件Wrapped,普通函数组件默认不接收父组件传递的ref属性。
  • React会把父组件传递给WrappedChild的ref绑定到Wrapped组件本身,但普通函数组件没有实例,所以childCompRef.current会是undefined,导致无法调用内部ChildComp的方法。
解决方法

需要让高阶组件withWrapper支持forwardRef,把父组件传递的ref透传给内部的原始ChildComp组件。具体修改如下:

import { Component, forwardRef, useRef, useImperativeHandle, React } from "react"

const getWrapper = () => {
  return class Test extends Component{
    public render() {
      return this.props.children;
    }
  }
}

// 改造withWrapper,用forwardRef接收并透传ref
const withWrapper = (
  Component: React.FunctionComponent | React.ForwardRefExoticComponent<React.RefAttributes<unknown>>
) => {
  return forwardRef((props: any, ref: React.Ref<any>) => {
    return React.createElement(getWrapper(), {}, 
      <Component {...props} ref={ref} />
    )
  })
}

const ChildComp = forwardRef((props, ref) => {
  useImperativeHandle(ref, () => ({
    showAlert() {
      alert("Hello from Child Component!~")
    },
  }))
  return <div></div>
})

const WrappedChild = withWrapper(ChildComp);

function App() {
  const childCompRef = useRef()
  return (
    <div>
      <button onClick={() => {
        // 增加判断避免空值报错
        if(childCompRef.current) {
          childCompRef.current.showAlert()
        }
      }}>Click Me</button>
      <WrappedChild ref={childCompRef} />
    </div>
  )
}
关键说明
  • 必须重新使用forwardRef改造高阶组件,只有通过forwardRef,组件才能接收并传递ref到内部嵌套的目标组件。
  • 透传ref后,父组件的childCompRef会指向useImperativeHandle暴露的方法集合,从而正常调用showAlert方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 20:47:23