How to use React.forwardRef in a class based component?

JavascriptReactjs

Javascript Problem Overview


I'm trying to use React.forwardRef, but tripping over how to get it to work in a class based component (not HOC).

The docs examples use elements and functional components, even wrapping classes in functions for higher order components.

So, starting with something like this in their ref.js file:

const TextInput = React.forwardRef(
    (props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />)
);

and instead defining it as something like this:

class TextInput extends React.Component {
  render() {
    let { props, ref } = React.forwardRef((props, ref) => ({ props, ref }));
    return <input type="text" placeholder="Hello World" ref={ref} />;
  }
}

or

class TextInput extends React.Component {
  render() { 
    return (
      React.forwardRef((props, ref) => (<input type="text" placeholder="Hello World" ref={ref} />))
    );
  }
}

only working :/

Also, I know I know, ref's aren't the react way. I'm trying to use a third party canvas library, and would like to add some of their tools in separate components, so I need event listeners, so I need lifecycle methods. It may go a different route later, but I want to try this.

The docs say it's possible!

>Ref forwarding is not limited to DOM components. You can forward refs to class component instances, too.

from the note in this section.

But then they go on to use HOCs instead of just classes.

Javascript Solutions


Solution 1 - Javascript

The idea to always use the same prop for the ref can be achieved by proxying class export with a helper.

class ElemComponent extends Component {
  render() {
    return (
      <div ref={this.props.innerRef}>
        Div has a ref
      </div>
    )
  }
}

export default React.forwardRef((props, ref) => <ElemComponent 
  innerRef={ref} {...props}
/>);

So basically, we are forced to have a different prop to forward ref, but it can be done under the hub. It's important that the public use it as a normal ref.

Solution 2 - Javascript

class BeautifulInput extends React.Component {
  const { innerRef, ...props } = this.props;
  render() (
    return (
      <div style={{backgroundColor: "blue"}}>
        <input ref={innerRef} {...props} />
      </div>
    )
  )
}

const BeautifulInputForwardingRef = React.forwardRef((props, ref) => (
  <BeautifulInput {...props} innerRef={ref}/>
));

const App = () => (
  <BeautifulInputForwardingRef ref={ref => ref && ref.focus()} />
)

You need to use a different prop name to forward the ref to a class. innerRef is commonly used in many libraries.

Solution 3 - Javascript

Basically, this is just a HOC function. If you wanted to use it with class, you can do this by yourself and use regular props.

class TextInput extends React.Component {
    render() {
        <input ref={this.props.forwardRef} />
    }
}

const ref = React.createRef();
<TextInput forwardRef={ref} />

This pattern is used for example in styled-components and it's called innerRef there.

Solution 4 - Javascript

This can be accomplished with a higher-order component, if you like:

import React, { forwardRef } from 'react'

const withForwardedRef = Comp => {
  const handle = (props, ref) =>
    <Comp {...props} forwardedRef={ref} />

  const name = Comp.displayName || Comp.name
  handle.displayName = `withForwardedRef(${name})`

  return forwardRef(handle)
}

export default withForwardedRef

And then in your component file:

class Boop extends React.Component {
  render() {
    const { forwardedRef } = this.props

    return (
      <div ref={forwardedRef} />
    )
  }
}

export default withForwardedRef(Boop)

I did the work upfront with tests & published a package for this, react-with-forwarded-ref: https://www.npmjs.com/package/react-with-forwarded-ref

Solution 5 - Javascript

Incase you need to reuse this in many difference components, you can export this ability to something like withForwardingRef

const withForwardingRef = <Props extends {[_: string]: any}>(BaseComponent: React.ReactType<Props>) =>
    React.forwardRef((props, ref) => <BaseComponent {...props} forwardedRef={ref} />);

export default withForwardingRef;

usage:

const Comp = ({forwardedRef}) => (
 <input ref={forwardedRef} />
)
const EnhanceComponent = withForwardingRef<Props>(Comp);  // Now Comp has a prop called forwardedRef

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionHan LazarusView Question on Stackoverflow
Solution 1 - JavascriptMr BrView Answer on Stackoverflow
Solution 2 - JavascriptSebastien LorberView Answer on Stackoverflow
Solution 3 - JavascriptŁukasz WojciechowskiView Answer on Stackoverflow
Solution 4 - JavascriptrpearceView Answer on Stackoverflow
Solution 5 - JavascriptoreporView Answer on Stackoverflow