← Back toMaster of React ref

Pass a ref to a child component using forwardRef()

Written byPhuoc Nguyen
Created
25 Oct, 2023
Tags
file input, React.forwardRef()
When working with React, there are instances where you'll need to pass a reference to a child component. This is helpful when you want the child component to access properties or methods of the parent component. One way to accomplish this is by using the `forwardRef()` method.
In this post, we'll explore this pattern by building an `Uploader` component that enables users to select a file from their computer. Let's get started!

Creating a simple file uploader component

File upload is a popular feature in web development that allows users to easily transfer files from their computer to a website or application. It's used across various industries, from e-commerce to healthcare to education, for uploading documents, images, videos, and more. In this post, we'll focus on building an Uploader component that simplifies the process of choosing a file from your computer. We'll keep it simple and won't dive into any complex file-related operations.
To upload one or more files, we simply need to use an input element with the `type` attribute set to `file`.
html
<input type="file" />
Although the file input is functional, it lacks style and customization options, making it difficult to integrate with our application's design language.
To solve this problem, we'll create an `Uploader` component that replaces the file input with a sleek and polished button. By using a button, we can take advantage of CSS to create a visually appealing and user-friendly interface. Buttons also allow us to add icons or text that provide additional context or instructions to the user.
In addition to the file input, the `Uploader` component renders a button with a more attractive appearance, like this:
tsx
<button className="uploader__button">Choose a file</button>
<input className="uploader__input" type="file" />
The `uploader__button` and `uploader__input` CSS classes are associated with the button and file input, respectively. We can easily customize their appearance by using these classes.
To hide the input, simply set its `display` property to `none`.
css
.uploader__input {
display: none;
}
You now have complete control over the appearance of the entire component. Simply modify the corresponding CSS class (`uploader__button`) to match your preferred style.
But wait, how can we trigger the file dialog when the file input is invisible? Easy - we handle the `click` event of the button instead. Within the handler, we'll trigger the `click` event of the input.
To make this happen, we first create a reference to the file input element using the `useRef()` hook. We then attach this reference to the file input using the `ref` attribute.
tsx
const inputRef = React.useRef();

// Render
<input className="uploader__input" ref={inputRef} />
To make our custom button open up the file dialog box when clicked, we need to define a function called `handleClick`. This function first gets a reference to the file input element we created earlier. If the reference exists, we call its `click()` method, which opens the file dialog box.
To make sure our custom button triggers the `handleClick` function, we add an event listener to it that listens for clicks. When the button is clicked, it calls the `handleClick` function, which in turn opens up the file dialog box for users to choose a file from their computer.
Here's the sample code to help you understand better:
tsx
const handleClick = () => {
const inputEle = inputRef.current;
if (inputEle) {
inputEle.click();
}
};

// Render
<button onClick={handleClick}>Choose a file</button>
Give this button a click and watch what happens. It'll open up a file dialog box. Don't worry, we're not going to do anything with the files you select. This is just a demonstration, after all.

Forwarding a reference

Let's say we want to replace the button in a certain situation. In addition to the usual way of adding a new prop to the `Uploader` for button customization, we can use the `forwardedRef()` method. In this section, we'll explore how to do that.
But first, let's assume that the `Uploader` component is placed within a container that also includes an SVG icon. No need to stress over the `handleClickContainer()` function, we'll go over it shortly.
tsx
<div className="container" onClick={handleClickContainer}>
<svg className="container__icon">
...
</svg>
<div className="container__uploader">
<Uploader />
</div>
</div>
To make the uploader completely invisible, we can add a CSS style to the class:
css
.container__uploader {
display: none;
}
We want users to be able to open the file dialog by clicking the container element, just like they can by clicking the button inside the uploader. To make this happen, we can create a reference to the `Uploader` component using the `useRef()` hook, and attach it to the `Uploader` component via the `ref` attribute.
tsx
const uploaderRef = React.useRef();

// Render
<Uploader ref={uploaderRef} />
When a user clicks on the container, the `handleClickContainer` function is triggered. This, in turn, invokes the `click` function of the main button within the `Uploader`.
ts
const handleClickContainer = () => {
const uploadBtn = uploaderRef.current;
if (uploadBtn) {
uploadBtn.click();
}
};
This message will appear in the browser Console until we can achieve the desired functionality through our imagination in React:
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use `React.forwardRef()`
In this case, React not only throws an error but also provides a helpful solution. It suggests using `forwardRef()`, which is designed for this use case. To implement this, you'll need to modify the Uploader component to support forwarding the ref.
ts
const Uploader = React.forwardRef((props, ref) => {
...
});
The `forwardRef()` method is a function that takes two parameters. The first parameter is `props`, which is the same as what you usually pass to the component. The second parameter is `ref`, which is the reference you want to expose so that other components can access the underlying node from outside.
In order to trigger the click function of the main button, we'll need to pass the `ref` down to it using the `ref` attribute.
tsx
const Uploader = React.forwardRef((props, ref) => {

// Render
<button ref={ref}>...</button>
});
Check out the demo below. To see the file dialog box, simply click on the entire container.

Conclusion

Using `forwardRef()` gives us more control over a child component's behavior and appearance by allowing us to pass a reference to it. It's also handy for integrating third-party libraries into our application when we don't have control over their implementation. This way, we can access the underlying node or component and perform the tasks we want.
This pattern is particularly useful when building reusable components that other developers can customize. With `forwardRef()`, we can expose specific parts of a component without revealing the nitty-gritty implementation details. This makes it easier for other developers to use our components in their projects.
If you found this post helpful, please consider giving the repository a star on GitHub or sharing the post on your favorite social networks 😍. Your support would mean a lot to me!

Questions? 🙋

Do you have any questions about front-end development? If so, feel free to create a new issue on GitHub using the button below. I'm happy to help with any topic you'd like to learn more about, even beyond what's covered in this post.
While I have a long list of upcoming topics, I'm always eager to prioritize your questions and ideas for future content. Let's learn and grow together! Sharing knowledge is the best way to elevate ourselves 🥷.
Ask me questions

Recent posts ⚡

Newsletter 🔔

If you're into front-end technologies and you want to see more of the content I'm creating, then you might want to consider subscribing to my newsletter.
By subscribing, you'll be the first to know about new articles, products, and exclusive promotions.
Don't worry, I won't spam you. And if you ever change your mind, you can unsubscribe at any time.
Phước Nguyễn