Skip to content

Instantly share code, notes, and snippets.

@gaearon
Created April 13, 2018 16:49
Show Gist options
  • Star 65 You must be signed in to star a gist
  • Fork 6 You must be signed in to fork a gist
  • Save gaearon/1a018a023347fe1c2476073330cc5509 to your computer and use it in GitHub Desktop.
Save gaearon/1a018a023347fe1c2476073330cc5509 to your computer and use it in GitHub Desktop.

React 16.2 and earlier doesn't yet support ref forwarding. If you need to expose a DOM ref to components above and can't migrate to React 16.3+ yet, you can use the following approach instead.

Expose a special prop on the child. This prop can be named anything other than ref (e.g. inputRef). The child component can then forward the prop to the DOM node as a ref attribute. This lets the parent pass its ref to the child's DOM node through the component in the middle.

This works both for classes and for functional components.

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <CustomTextInput inputRef={this.inputElement} />
    );
  }
}

In the example above, Parent passes its class property this.inputElement as an inputRef prop to the CustomTextInput, and the CustomTextInput passes the same ref as a special ref attribute to the <input>. As a result, this.inputElement.current in Parent will be set to the DOM node corresponding to the <input> element in the CustomTextInput.

Note that the name of the inputRef prop in the above example has no special meaning, as it is a regular component prop. However, using the ref attribute on the <input> itself is important, as it tells React to attach a ref to its DOM node.

This works even though CustomTextInput is a functional component. Unlike the special ref attribute which can only be specified for DOM elements and for class components, there are no restrictions on regular component props like inputRef.

Another benefit of this pattern is that it works several components deep. For example, imagine Parent didn't need that DOM node, but a component that rendered Parent (let's call it Grandparent) needed access to it. Then we could let the Grandparent specify the inputRef prop to the Parent, and let Parent "forward" it to the CustomTextInput:

function CustomTextInput(props) {
  return (
    <div>
      <input ref={props.inputRef} />
    </div>
  );
}

function Parent(props) {
  return (
    <div>
      My input: <CustomTextInput inputRef={props.inputRef} />
    </div>
  );
}

class Grandparent extends React.Component {
  constructor(props) {
    super(props);
    this.inputElement = React.createRef();
  }
  render() {
    return (
      <Parent inputRef={this.inputElement} />
    );
  }
}

Here, the ref this.inputElement is first specified by Grandparent. It is passed to the Parent as a regular prop called inputRef, and the Parent passes it to the CustomTextInput as a prop too. Finally, the CustomTextInput reads the inputRef prop and attaches the passed ref as a ref attribute to the <input>. As a result, this.inputElement.current in Grandparent will be set to the DOM node corresponding to the <input> element in the CustomTextInput.

@hejiaji
Copy link

hejiaji commented Dec 18, 2018

How can i get the reference in current component (need to use it here) then pass it to parent???

@Sahil5963
Copy link

great boi

@Lexiebkm
Copy link

Thank you. I try to use forwardRef to get access to child's DOM, but the child component contains multiple form input element. I get stuck.
When I use this alternative method, the problem is solved. My app will have several similar patterns of data binding between a datagrid and a form. So using this method will be convenient for me.

@pratik14
Copy link

There is no mention of any direct benefit of using forwardRef in React Docs

The only benefit I can think of is to keep consistent code i.e instead of using prop variable like inputRef, innerRef... for passing ref, we can consistently use only one variable ref in case of forwardRef. React-redux uses forwaredRef, to attach ref with Wrapper component.

@zz-james
Copy link

what's the prop-type for the forwarded ref?

@SamanShafigh
Copy link

the value of current inside that parent and grandparent for me turns out to be null and not the input that child renders. Has anyone tried this?

@benwzj
Copy link

benwzj commented Oct 16, 2020

understand!

@pupretiDev
Copy link

pupretiDev commented Nov 16, 2020

With respect to the comment made by @gaearon - "This works even though CustomTextInput is a functional component. Unlike the special ref attribute which can only be specified for DOM elements and for class components, there are no restrictions on regular component props like inputRef."

This seems little misleading. Even in the case of React.forwardRef, we can still use 'ref' attribute on parent functional component but the child should finally have a 'ref' attribute equated to forwaredRef value and this child should either be a html DOM element or React class element. Hint: See the example below.
Also this alternative prop based ref forwarding discussed above has the same fundamental. The final child , whatever be the depth, that gets the 'ref' attribute ( In this pattern 'ref' is used only once and that too at the final child level), has to be a html DOM element or React class element.
Therefore I dont see any advantage of this alternative approach over Ref forwarding wrt the statement of the author.
However, I completely agree with the benefit of multilevel ref forwarding available with this approach.

Example-

/* Parent is a functional component whose child is html input */

const Parent= React.forwardRef((props, forwardedRef) => { 

function handleClick(){
	alert ("clicked");
}
  return (
    <input type="text" ref={forwardedRef}/>      // child is  having a 'ref' attribute
  );
});


const App = () => {
  const myRef =useRef(null);
  const callMe = () => {    
    myRef.current.focus();
  };
  return (
   <div >
      <Parent ref={myRef}/>    // parent is also referred by 'ref' attribute
      <button onClick={callMe}>Test</button>
    </div>
  );
};

@cumt-robin
Copy link

got it !

@umardevid
Copy link

umardevid commented Oct 9, 2021

With respect to the comment made by @gaearon - "This works even though CustomTextInput is a functional component. Unlike the special ref attribute which can only be specified for DOM elements and for class components, there are no restrictions on regular component props like inputRef."

This seems little misleading. Even in the case of React.forwardRef, we can still use 'ref' attribute on parent functional component but the child should finally have a 'ref' attribute equated to forwaredRef value and this child should either be a html DOM element or React class element. Hint: See the example below. Also this alternative prop based ref forwarding discussed above has the same fundamental. The final child , whatever be the depth, that gets the 'ref' attribute ( In this pattern 'ref' is used only once and that too at the final child level), has to be a html DOM element or React class element. Therefore I dont see any advantage of this alternative approach over Ref forwarding wrt the statement of the author. However, I completely agree with the benefit of multilevel ref forwarding available with this approach.

Example-

/* Parent is a functional component whose child is html input */

const Parent= React.forwardRef((props, forwardedRef) => { 

function handleClick(){
	alert ("clicked");
}
  return (
    <input type="text" ref={forwardedRef}/>      // child is  having a 'ref' attribute
  );
});


const App = () => {
  const myRef =useRef(null);
  const callMe = () => {    
    myRef.current.focus();
  };
  return (
   <div >
      <Parent ref={myRef}/>    // parent is also referred by 'ref' attribute
      <button onClick={callMe}>Test</button>
    </div>
  );
};

i wan to access current object forwardedRef.current
but caussing typescript error: current does not exist on type

@lgenzelis
Copy link

Anyone knows of any benefit of using React.forwardRef over this simpler approach?

@realamirhe
Copy link

realamirhe commented Jan 15, 2022

@lgenzelis Although it seems to be opinionated, here are some points that make sense.

@lgenzelis
Copy link

Thanks @amirHossein-Ebrahimi ! That answer seems to make the case against React.forwardRef, though, not for it. The only points it mentions in favor of forwardRef are:

  • uniform access API for DOM nodes, functional and class components
  • ref attribute does not bloat your props API, e.g. if you provide types with TypeScript

They seem really weak for me. I just feel silly that I've been using forwardRef for a long time without considering this simple alternative. Like, if these are the only thing sin favor of forwardRef, I won't use it again 😅

@yuri-scarbaci-lenio
Copy link

I have seen performance hit when using forwardRef (even with extreme memoization) so I stick to props custom ref
you can read more about it here facebook/react#13456

@asiraky
Copy link

asiraky commented Dec 4, 2023

Many of you are discounting the benefits of using forwardRef; having a consistent predictable api. If I'm using a 3rd party library that needs a ref to my components, it may be simply expecting that it can supply a ref prop to my components. Using inputRef or some other prop to pass the ref on, the 3rd party lib wont get a ref to the underlying dom node. There is only 1 legitimate case IMO not to use forwardRef and that is if you are using typescript and you need to add a generic to your component that uses forwardRef. It's not impossible, its just a pain in the ass, and the above can work. Problem is it then falls apart for the reasons I explained.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment