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

