How to access ref of child component with wrappers in React

402 Views Asked by At

I'm trying to learn how forwardRef works in React and playing around with it based on official docs. I understand how it works with this basic example:

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

When I click the button in App, it shows the alert, so far so good.

However based on my real use case, I have to do bunch of wrappings on top of my ChildComp, which could be simplified as the following. Here is the Codesandbox link

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>
  )
}

It shows childCompRef.current is undefined when I click on the button. The wrappers cannot be removed due to legacy reasons. I'm wondering where in the process does it lose the "ref" and how could I make it work as before in this case? Do I need to wrap forwardRef somewhere else?

1

There are 1 best solutions below

0
Unmitigated On

You need another layer of forwardRef:

const withWrapper = () => {
    return forwardRef((props, ref) => React.createElement(getWrapper(), {}, <Component {...props} ref={ref}/>));
}