React Development Made Easy: Working With The UseRef Hook
Published: 2023-12-12 17:45:33

by [Joel Ojerinde](https://blog.openreplay.com/authors/joel-ojerinde)


<blockquote><em>
What is the `useRef` React hook? This article will give you information about it: You'll learn what useRef is, how to use it, why it's crucial for web development using React, and where to use it. You will also see the `forwardRef` and `useImperativeHandle` hooks in action.
</em></blockquote>


<div style="background-color:#efefef; border-radius:8px; padding:10px; display:block;">
<hr/>
<h3><em>Session Replay for Developers</em></h3> 
<p><em>Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data.</em></p>
<img alt="OpenReplay" style="margin-top:5px; margin-bottom:5px;" width="768" height="400" src="https://raw.githubusercontent.com/openreplay/openreplay/main/static/openreplay-git-hero.svg" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<p><em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em><p>
<hr/>
</div>

[useRef](https://react.dev/reference/react/useRef) is a built-in function that allows you to create a reference to a `DOM`(Document Object Model) element, enabling direct interaction with it.

Why is `useRef` important for `DOM` manipulation and element access?

- **Efficiency**: `useRef` minimizes unnecessary re-renders, improving performance when working with DOM elements.
- **Preserving State**: It allows you to persist and maintain element states across renders.
- **Child Component Communication**: Enables parent components to share element references with child components.
- **React Ecosystem Compatibility**: Integrates smoothly with React's declarative approach and third-party libraries.
- **Cleaner Code**: Promotes clean and readable code by encapsulating DOM interactions within React components.
- **Improved Debugging**: Aligns with React's component lifecycle, simplifying debugging and issue resolution.

`useRef` is a valuable hook in React development because it allows you to directly access and interact with `DOM` elements, and a significant advantage of using `useRef` over traditional DOM manipulation lies in its ability to optimize performance. `useRef` ensures that modifications to the DOM do not result in complete re-renders of the component, thus enhancing overall performance.

In what real-world situations can `useRef` be effectively used?

`useRef` can be used in various scenarios, and I'll highlight a few:

- Animating Elements
- Creating Timer
- Interacting with Audio and Video Element
- Focus Management
- Integration with Third-Party Libraries

## Getting Started with useRef

To begin, Let's build a basic feature for focus management. No need to delay – let's dive right in.

Like with any other built-in hook, the first step is to import the hook from React, as demonstrated below.

```javascript
import { useRef } from "react";
```

Next, we invoke the hook within our functional component. It's important to note that React hooks are only valid within functional components. To explore the rules and guidelines governing the use of React hooks, you can follow this [link](https://react.dev/warnings/invalid-hook-call-warning).

```javascript
function ParentComponent() {
 let initialValue = null;
 const inputRef = useRef(initialValue);
}
```

Let's walk through the code snippet above. In this functional component, I declare an `initialValue` variable and assign it `null` because, at this point, it doesn't reference any DOM elements. Keep in mind that the choice of the initial value may vary based on your specific use case.

Next, I called the hook and provided the initial value as an argument. When you invoke the `useRef` hook, it returns a reference, an object featuring a single property named `current`. It's important to note that this reference object is initially set to the provided initial value.

Before we continue, let's optimize the code into a single line and console.log the reference object to confirm the output.

```javascript
function ParentComponent() {
 const inputRef = useRef(null);
 console.log(inputRef); // {current:null}
}
```

In the revised code snippet below, I linked the reference to the input field using the `ref` attribute provided by React. The `ref` attribute is built into React and does not require additional imports.

```javascript
function ParentComponent() {
 const inputRef = useRef(null);

 const handleFocus = () => {
   inputRef.current.focus();
 };

 return (
   <div>
     <input
       ref={inputRef}
       type="text"
       placeholder="Click the button to focus me"
     />
     <button onClick={handleFocus}>Focus Input</button>
   </div>
 );
}
```

Did I call `.focus` on `null`, considering that `null` is the initial value assigned to the `current` property? No, I didn't. Once the component is rendered, React will populate the `current` property of the `inputRef` with the DOM element it is connected to, which, in this case, is the input field. Clear? If not, let's examine the output together.

Keep in mind that this action occurs after the component has finished mounting. Therefore, we will employ the useEffect hook to inspect the value. Why choose the useEffect hook? It's because the useEffect hook runs after the component has successfully mounted.

```javascript
import { useRef, useEffect } from "react";

function ParentComponent() {
 const inputRef = useRef(null);

 const handleFocus = () => {
   inputRef.current.focus();
 };

 useEffect(() => {
   import { useRef } from "react";
   console.log(inputRef.current); //<input type="text" placeholder="Click the button to focus me"/>
 }, []);

 console.log("I am rendered only once!");

 return (
   <div>
     <input
       ref={inputRef}
       type="text"
       placeholder="Click the button to focus me"
     />
     <button onClick={handleFocus}>Focus Input</button>
   </div>
 );
}
```

I hope you haven't forgotten to import the `useEffect` hook. If you have, remember that it will trigger an error, and you need to import built-in hooks from `react` to resolve the error.

Let's proceed with our example. When you click the button, the input field will be focused. It's important to note that this happens without triggering a complete component re-render, making it a performance-efficient approach that outperforms traditional DOM manipulation. Let's test the code now.

Before clicking on the button:

After clicking on the button:

Open your developer console, and you'll notice that we have only one output, regardless of how many times you click the button. This demonstrates that the component doesn't undergo re-renders.

## Implementing useRef and Sharing with Child Components

Now that we've grasped the concept of `useRef` and its usage in the parent component, let's explore how we can pass it to a child component.

Passing a `ref` to a child component serves several purposes, including:

- Accessing DOM Elements: It allows you to directly access and manipulate the child component's DOM elements.
- Facilitating Parent-Child Communication: It enables seamless communication between parent and child components.
- Integrating with Third-party Libraries: It aids in integrating with third-party libraries that need direct access to the DOM.
- Implementing Custom Hooks: ref can implement custom hooks, encapsulating complex logic.
- Form Validation and Control: It's useful for managing form validation and control within child components.

Let's consider some examples that illustrate some of the mentioned purposes.

Allow me to provide an overview of what we're about to create. In this example, the child components will render a video controlled by the parent component. This will involve discussing another hook, [useImperativeHandle](https://react.dev/reference/react/useImperativeHandle), and utilizing the [forwardRef](https://react.dev/reference/react/forwardRef) wrapper.

Remember that the child component doesn't expose its DOM node to its parent component; this is where `forwardRef` comes in. Also, instead of allowing the parent component to access the entirety of the child component node, we will make use of the `useImperativeHandle` to expose custom value.

Let's see all this in action. We will start by creating the parent component.

```javascript
import React, { useRef } from "react";
import exampleVideo from "./asset/example.mp4";

function ParentComponent() {
 const videoRef = useRef(null);

 const handlePlay = () => {
   videoRef.current.play();
 };

 const handlePause = () => {
   videoRef.current.pause();
 };

 return (
   <div>
     <ChildComponent ref={videoRef} source={exampleVideo} />
     <button onClick={handlePlay}>Play Video</button>
     <button onClick={handlePause}>Pause Video</button>
   </div>
 );
}
```

Let's walk through the provided code snippet. As previously mentioned, we're importing `useRef` from React, and we're also importing a video file for this example.

In the ParentComponent, we create a reference (videoRef) using the `useRef` hook and initialize it with a value of `null`.

I also define two functions:

- handlePlay function, which, when invoked, plays the video element referred to by videoRef using the play() method.
- handlePause function, which, when invoked, pauses the video element referred to by videoRef using the pause() method.

Within the return block, we have a ChildComponent that receives the `videoRef` as a `ref` and the exampleVideo as a source. This implies that the ChildComponent is expected to be a component that can handle video rendering and playback using the `ref` and source props.

We also have two buttons: "Play Video" and "Pause Video". When clicked, they trigger the `handlePlay` and `handlePause` functions, allowing you to control the video playback within the ChildComponent.

Let's go ahead and create the ChildComponent.

```javascript
import { forwardRef } from "react";

const ChildComponent = forwardRef((props, ref) => {
 return (
   <video ref={ref} controls>
     <source src={props.source} type="video/mp4" />
   </video>
 );
});
```

In this snippet above, we define a ChildComponent using the `forwardRef` function from React. `forwardRef` is a way to pass a `ref` from a parent component to a child component. In this case, ChildComponent is expected to receive a ref from its parent, which will be used to reference the `<video>` element it renders.

We imported `forwardRef` from React.

We defined the ChildComponent using the forwardRef function. This function takes a callback that receives two arguments: `props` and `ref`.

Inside the component, we returned a `<video>` element. The `ref` attribute is set to the `ref` passed from the parent component. This allows the parent component to access the `<video>` element and control its playback.

The `<video>` element has the controls attribute, which provides standard video player controls like play, pause, and volume.

Inside the `<video>` element, we have a `<source>` element. It specifies the video source using the props.source value, which is passed from the parent component. In this case, it's expected to be an MP4 video file.

Here is the output:

https://github-production-user-asset-6210df.s3.amazonaws.com/104495751/278117529-cd1dd780-3450-4497-bc6b-16a55e27617f.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20231212%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231212T173944Z&X-Amz-Expires=300&X-Amz-Signature=f71ea31fae86009570c52d353f5281d7522c93facfc2616b323bc9708c043694&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=693873164

Are you curious about where the `useImperativeHandle` hook fits into the picture? Let's explore it together. Stick around for the answer.

The previous way we implemented the feature works fine, as seen in the output above, but it's only necessary when the ParentComponent requires full access to the ChildComponent's `DOM` node. In this scenario, we need access to the pause and play methods of the video node, and that's where `useImperativeHandle` becomes relevant.

The `useImperativeHandle` hook enables us to provide a handle with only the methods the parent component should be able to call.

Let's continue with the implementation.

```javascript
const ChildComponent = forwardRef((props, ref) => {
 const childVideoRef = useRef();

 useImperativeHandle(ref, () => ({
   play: () => {
     childVideoRef.current.play();
   },
   pause: () => {
     childVideoRef.current.pause();
   },
 }));

 return (
   <video ref={childVideoRef} controls>
     <source src={props.source} type="video/mp4" />
   </video>
 );
});
```

In this snippet above, the ChildComponent has been updated to utilize the `useImperativeHandle` hook to expose specific methods to the parent component, allowing control over the video playback.

We still use the `forwardRef` function to enable the parent component to pass a `ref` to this child component.

Inside the ChildComponent, a new `childVideoRef` is created using the `useRef` hook. This reference is used to store a reference to the `<video>` element rendered within the child component.

The `useImperativeHandle` hook is used to define what gets exposed through the `ref` passed by the parent component. `useImperativeHandle` takes two arguments: the `ref` and a callback function that defines what methods or properties should be exposed to the parent component.

In the callback function, two methods are defined: play and pause. These methods use the `childVideoRef` to call the `play()` and `pause()` methods on the `<video>` element, respectively. This effectively allows the parent component to trigger these actions on the video element within the child component.

Remember that the methods you expose through `useImperativeHandle` don't have to perfectly match the corresponding `DOM` methods.

Inside the return block, the `<video>` element uses the `childVideoRef` as its `ref`. This means that the ref passed by the parent component is associated with this specific `<video>` element.

The `<video>` element also includes the controls attribute, which provides standard video player controls, and a `<source>` element to specify the video source, just like in the previous version of the code.

Below is the complete code for this example:

```javascript
import ReactDOM from "react-dom/client";
import "./index.css";

import React, { useRef, forwardRef, useImperativeHandle } from "react";
import exampleVideo from "./asset/example.mp4";

const ChildComponent = forwardRef((props, ref) => {
 const videoRef = useRef();

 useImperativeHandle(ref, () => ({
   play: () => {
     videoRef.current.play();
   },
   pause: () => {
     videoRef.current.pause();
   },
 }));

 return (
   <video ref={videoRef} controls>
     <source src={props.source} type="video/mp4" />
   </video>
 );
});

const ParentComponent = () => {
 const videoRef = useRef();

 const handlePlay = () => {
   videoRef.current.play();
 };

 const handlePause = () => {
   videoRef.current.pause();
 };

 return (
   <div>
     <ChildComponent ref={videoRef} source={exampleVideo} />
     <button onClick={handlePlay}>Play Video</button>
     <button onClick={handlePause}>Pause Video</button>
   </div>
 );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<ParentComponent />);
```

Here is the output:

https://github-production-user-asset-6210df.s3.amazonaws.com/104495751/278117529-cd1dd780-3450-4497-bc6b-16a55e27617f.mp4?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20231212%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20231212T173944Z&X-Amz-Expires=300&X-Amz-Signature=f71ea31fae86009570c52d353f5281d7522c93facfc2616b323bc9708c043694&X-Amz-SignedHeaders=host&actor_id=0&key_id=0&repo_id=693873164

Lastly, Let us see an example where we expose a custom method using the `useImperativeHandle`.

```javascript
import ReactDOM from "react-dom/client";
import "./index.css";
import React, { useRef, forwardRef, useImperativeHandle } from "react";

const ChildComponent = forwardRef((props, ref) => {
 const inputRef = useRef();

 useImperativeHandle(ref, () => ({
   focus: () => {
     inputRef.current.focus();
   },
   validate: () => {
     return inputRef.current.value.trim() !== ""; // Custom validation logic
   },
 }));

 return <input type="text" ref={inputRef} {...props} />;
});

const ParentComponent = () => {
 const parentInputRef = useRef();

 const handleFocusTextInput = () => {
   parentInputRef.current.focus();
 };

 const handleValidate = () => {
   const isTextInputValid = parentInputRef.current.validate();
   console.log(isTextInputValid);
 };

 return (
   <div className="app">
     <ChildComponent ref={parentInputRef} placeholder="Enter text" />
     <button onClick={handleFocusTextInput}>Focus Input</button>
     <button onClick={handleValidate}>Validate Input</button>
   </div>
 );
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<ParentComponent />);
```

Let me walk you through the snippet above.

- In the `ChildComponent`, we initialized a new reference (`inputRef`) and attached it to the rendered `<input>` element. This initialization is done in the child component to prevent the parent component from directly accessing the entire input element.

- The `useImperativeHandle` defines and exposes two methods, `focus` and `validate`, through the `ref`. These methods allow the parent component to interact with the child input element. The `focus` function sets focus on the input field, and the `validate` function performs a custom validation logic, checking if the input value is not an empty string.

- In `ParentComponent`, another `ref` called `inputRef` is created.

- The `handleFocusTextInput`` function is called when the "Focus Text Input" button is clicked. This function uses `inputRef.current.focus()` to trigger the focus function defined in the ChildComponent, which focuses on the input field.

- The `handleValidate` function is called when the "Validate Text Input" button is clicked. It uses `inputRef.current.validate()` to trigger the validate function defined in the ChildComponent. The result of the validation is logged into the console.

Below is the output:

Before clicking on the focus input button:

After clicking on the focus input button:

After clicking on the Validate input button:
With no text:

With text:

And that is a wrap.

## Conclusion

In this article, we've explored the usage of the `useRef` hook in React, its ability to manage `DOM` elements, and how to pass and control references between parent and child components using forwardRef and useImperativeHandle.

You can develop an example of using `useRef` with third-party libraries, following a similar approach to what we've demonstrated. Be sure to refer to [React's documentation](https://react.dev/reference/react/useRef) for in-depth insights. Also, practicing on your own is crucial for effective learning and mastering this concept.