React.forwardRef is already possible without it, so what's the use of it?
Asked Answered
C

3

15

I'm confused on the point of React.forwardRef. As explained in its documentation, I understand that its main use is for a Parent Component to gain access to DOM elements of the Child Component. But I can already do that without even having to use it.

Here is a code example that you can plug into CodeSandbox and see that it works:

import React, {useRef, useEffect} from "react";
import "./styles.css";

const ChildComponent = (props) => {

  useEffect( ()=> {
    props.callbackFunction()
  })

  return(
    <div ref={props.fRef}>
      {"hello"}
    </div>
  )
}


export default function App() {
  const callbackFunction = () => {
    console.log("The parent is now holding the forwarded ref to the child div: ")
    console.log(forwardedRef)
  }

  const forwardedRef = useRef(null)

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <ChildComponent name="gravy" callbackFunction={callbackFunction} fRef={forwardedRef}/>
    </div>
  );
}

Or here's the embed of this example. Honestly, I'm kind of new to this and I don't know exactly how embeds work and whether someone fiddling with the embed changes my original Sandbox or not, so I was hesitant to put it. But here it is. Example Forwarding Ref

In the example, the parent App() component successfully passes a ref to the child which the child attaches to its rendered div. After it renders, it calls a callback function to the parent. The parent then does a console log where it proves that its forwarded ref now has a hold of the child's div. And this is all done without React.forwardRef.

So what then is the use for React.forwardRef?

Cineraria answered 31/3, 2020 at 23:37 Comment(1)
A very good and wise question. thanks.Substantialism
X
15

You're absolutely right that you can do what you've described. The downside is that you're forced to expose an API (ie: the fRef prop) for it to work. Not a huge deal if you're a solo developer building an app, but it can be more problematic eg. if you're maintaining an open-source library with a public API.

In that case, consumers of the library won't have access to the internals of a component, meaning you'd have to expose it for them somehow. You could simply do what you're suggesting in your example and add a named prop. In fact, that's what libraries did before React 16.3. Not a huge deal, but you'd have to document it so people know how to use it. Ideally, you'd also want some kind of standard that everyone used so it wasn't confusing (many libraries used the innerRef naming convention), but there'd have to be some consensus around that. So all doable, but perhaps not the ideal solution.

Using forwardRef, passing a ref to a component just works as expected. The ref prop is already standardized in React, so you don't need to go look at docs to figure out how to pass the ref down or how it works. However, the approach you describe is totally fine and if it meets your needs, by all means go with that.

Xyloid answered 31/3, 2020 at 23:57 Comment(2)
I have one more question. If a component is made with React.forwardRef, then how would you get a ref to that actual component instead of its inner rendered elements? Because when you attach a ref to the actual component, it will assumedly forward it to the rendered element in its child since its assumedly set up for that purpose. So it feels like a heavy tradeoff to no longer be able to have a ref simply to that component. Am I misunderstanding this?Cineraria
forwardRef is prob most useful with simple components that wrap HTML elements. If you have a complex component where you need to expose more than one thing, then you might use named props in that case in addition to (or instead of) a normal ref. For example a typeahead component composed of an input and menu might expose inputRef + menuRef (+ ref to access the component itself). You could also provide methods on the component to access the underlying elements (ref.current.getInputNode() + ref.current.getMenuNode()).Xyloid
E
1

As mentioned in the docs , it's useful for highly reusable components, meaning components that tend to be used like regular HTML DOM elements.

This is useful for component libraries where you have lots of "leaf" components. You've probably used one like Material UI.

Example:

Let's say you're maintaining a component library.

You create a <Button/> and <Input/> component that maybe just adds some default styling.

Notice how these components literally are just like regular HTML DOM elements with extra steps.

If these components were made to be used like regular HTML DOM elements, then I expect all the props to be the same, including ref, no?

Wouldn't it be tedious if to get the button ref from your <Button/> component I'd have to get it through something like fRef or buttonRef ?

Same with your <Input/>, do I have to go to the documentation just to find out what ref to use and it's something like inputRef ? Now I have to memorize?

Getting the ref should be as simple as <Button ref={}/>

Problem

As you might know, ref will not get passed through props because, like key, it is handled differently by React.

Solution

React.forwardRef() solves this so I can use <Button ref={}/> or <Input ref={}/>.

Esperanzaespial answered 1/4, 2020 at 0:40 Comment(0)
I
1

It is useful when you want two way interaction in the code, because without the forwardRef we have only one way direction, from child to parent. No way to initiate a function call from the parent. If you want to initiate a state change in the child, you have to change the state in the parent. With this tool we can make own functions and properties that are exposed to outer objects, like public methods and properties in a class.

Without forwardRef:
The parent can't tell to his child to go out and play basketball. But the child when he wants to play, can say to his parent that: Hey I'm playing basketball now! (This was a callback).

With forwardRef:
The parent can initiate an action on the child. So just telling to him: Go out and play basketball now! (This was the forwardRef action) Then the child can give an answer to the parent: Ok, I'm playing basketball now! (This was a callback)

A Real life example:
It is useful when a surface want to rerender an object without rerender wholeself. If I have 4 input field and another 2 field that values are computed from the other 4 content, my only way to refresh the computed data if I rerender all of the fields, so the parent. After render I loose focus from the last selected field and sometimes got screen vibration etc... But, with forwardRef when the user change a value I just call the function from one of the computed fields and it is rerendering only himself. So the surface capable to do continous realtime refresh.

Immerge answered 11/7 at 14:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.