React forwardRef Demystified

Pro Members Only

This lesson is available if you have an active subscription.

Alternatively, some member might be able to gift it to you.

If you already have a subscription, you can sign in.

React forwardRef Demystified

Subscription Required

You must have an active subscription to access this content.
If you already have a subscription, you can sign in.

use case for React forwardRef

The main use case for React forwardRef is that it allows custom function components to accept a ref prop that they can assign to an internal browser native element.

This is demonstrated below where our custom FancyField accepts a ref prop and assigns it to the input element.

FancyField.tsx
App.tsx
import React from "react";

const fieldStyle = {
display: "flex",
flex: "1",
flexDirection: "column",
gap: "10px",
justifyContent: "center",
} as const;

type FancyFieldProps = {
id: string;
label: string;
value: string;
onChange: (value: string) => void;
};

export const FancyField =
React.forwardRef(function FancyField(
{ id, label, value, onChange }: FancyFieldProps,
ref: React.Ref<HTMLInputElement>
) {
return (
<div style={fieldStyle}>
<label htmlFor={id}>{label}</label>
<input
id={id}
ref={ref}
value={value}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);
});

Reference Notes

  • React.forwardRef takes a function component as an argument.
  • The function component is passed in the ref as a second argument.
    • You can provide a type for this ref using React.Ref<> generic type.
  • You should name the function component you pass to forwardRef to ensure it shows nicely in the React developer tools.
javascript
typescript
react
playwright

Enjoy free content straight from your inbox 💌

No spam, unsubscribe at any time.

Transcript

00:00

If we've looked at using refs and react with native browser elements. And the next question you might have is how can you use a rev prop when you are building your own custom components? Unfortunately, it's not as straightforward as adding a new prop, but it's pretty simple once you understand how to do so, and that is exactly what people cover in this tutorial. So let's go to demonstrate the need for riffs. Consider a very simple application. Hey, we have a simple style that is going to take up the entire screen for the individual fields. And then within the field we have a nice simple flex layout, which is going to layout its children in a simple column

00:34

with a gap of 10 pixels between them. And then using justify content, we are centering it vertically within the screen. The CSS is honestly not the focus of this session, but it's good to look at it from time to time. The key focus is our application component, which will have two simple fields and we will manage the state of this first name and the last name field with a simple use state for the input elements within these fields. We will create simple refs using reacts built-in used ref hook, and then within utility functions for focus, we can use these refs to get access

01:06

to the underlying input elements so we can invoke focus on them. Within the ui, we have a full screen section for the first field, which will contain the label and the input. And then we have the same process for the second field, which will be last name. The key thing to note over here is that we are using the ref to get access to the input elements for the first name and the last name inputs. Finally, in another full screen section, we will have two simple buttons that allow us to trigger focus on the first name or the last name input fields. If we jump into the ui, we get a very similar

01:39

to Typeform experience where we get a full screen first field where the user can provide their first name, and then we can scroll to the second field where the user can provide the second name. And then we have these buttons that allow the user to directly jump to a particular field. So using these buttons, they can directly jump to the first name or the last name in case they want to make any edits. And this all works perfectly fine. However, there is quite a bit of duplication between the two fields. So we decide that we're going to build a new fancy field which encapsulates all of this complexity for us. We have the same style for the field,

02:12

which we previously saw in the app component, and then we define the props that this field is going to work with. We are going to take the id, the label, the value and the own change, and our component function is going to render exactly what we previously saw within the app component. We restructured the past and props, and then we have a diviv of field style containing the label and the input elements. So let's use this fancy field within our application component. We import it from the fancy field module. Within the first full screen section, we render the fancy field for first name,

02:46

and then in the second full screen section, we render another fancy field for the last name. Now, the only thing that is missing from our fancy field right now is the support for a ref prop. Now we can try to add that using what we already Know about props. We pass in the first name ref for the first fancy field and the last name ref for the second fancy field. And of course right now it's going to give an error, but then we jump into the fancy field component and add a ref member of type React ref for an HTML input element. This gets rid of that error that we were seeing.

03:18

And then of course the next step would be to destructure it from the past in props and then assign it as a prop for the underlying input element. Unfortunately for us, however, this is not going to work and to prove that we are also going to log out the value of the past in the ref object to the console with this attempt at trying to use a ref through the standard props complete. When we jump into the browser, you can see that the logs that we are getting are undefined, along with lots of errors being thrown by React. And this is where the forward ref function comes into play.

03:50

It allows you to create a separate channel to pass in the ref prop. So let's get rid of our poor attempt at trying to use the existing infrastructure. We remove the ref from the props and then we remove it from the function arguments. The official way to use a ref with custom components is to wrap our function with React forward ref, and this allows our function to accept a second parameter for the ref, which in our case is going to be a ref for an HTML input element. And now if you jump back to the application, you can see that those errors have gone away

04:22

and if we interact with it, for example, provide the first name and the last name, the buttons that are wire to invoke focus on the ref element are going to function. And the way this is working of course, is that ref is getting passed to a component and our component is handing it off to the input element. An issue with the forward ref function is that by default, it doesn't present nicely within the React developer tools, but this is quite easy to fix with some additional code. If you open up the components panel within the React developer tools, this is what our component tree looks like, we can see the app and then we see underscore C

04:56

for the individual fancy field components. And of course, this is not ideal. Now the reason why this happens is because forward ref only captures the name of the function that is passed in, and right now we are passing in an anonymous function, but of course, we can give this function the same name that we are giving to the external variable. And this is also why we didn't use an error function over here because just in case you do not know, error functions within JavaScript are always anonymous. That is, they are always unnamed. Now that we have provided a function with a nice name,

05:29

if we jump back to the developer tools, you can see that this name shows up within the component tree. In summary, the React Forward ref function allows you to preserve revs when creating your own custom wrapping components. As always, thank you for joining me and I'll see you in the next lesson. We will look at the related concept of imperatives.