Understanding React useRef and Utilizing its superpower๐Ÿ’ช

ยท

6 min read

Are you tired of feeling like a helpless bystander in your React application? Do you wish you had more control over the DOM nodes that make up your user interface? Well, fear not my friend, because there's a new sheriff in town - useRef! With this handy tool, you can take the reins and access DOM nodes like never before.

If you're here, chances are you've heard about React Hooks, and probably used some of the famous ones like useState and useEffect, but useRef is new ground for you.

If React were a big bowl of candies, then hooks are the latest additions, very chewy candies with great taste!_ Ohans Emmanuel

In this article, I'll discuss what useRef is, how you can use it, familiar cases you will most often use it, and common pitfalls to avoid. So let's get right into it.

What is useRef?๐Ÿค”

I am a big fan of docs, so let's see what React docs say about useRef; "useRef is a React Hook that lets you reference a value thatโ€™s not needed for rendering". More clearly, it means useRef lets you refer to a value, that is not needed for your component to render or re-render.

Let's break it down further, let's say you have a toy you want to play with, but you only have specific reasons or times you play with that toy. So when you are not playing with it, you store it away, but bring it out later when it is time to play with it again.

Photo by Ksenia Chernaya

useRef is like a special box for you to put your toys in and a way to remember where exactly you put your toy so you can easily find it when next you need to play with it.
useRef is used in React to help you remember certain things about your application that won't necessarily be needed for rendering. For example, maybe you want to remember what is being typed into a form field, or you want to remember the height of an element on a web page.

How to use it ๐Ÿ› 

Like everything in life, there is an instruction manual. The same applies to useRef, and I will show you how to use it.

The basic way to initialize useRef is like this:

const myRef = useRef(initialValue)

useRef returns an object that is mutable with a single property current.

{current: initialValue}, current is set to the initialValue you passed into the useRef. You can pass the ref object into as ref in your React application.

You're probably curious as to why we're not just storing the object in a variable instead. This is because the value stored in your myRef.current is persisted for the entire lifetime of the component, which means the value does not update even when your component re-renders.

const myRefExample =()=>{
const myRef = useRef("React hooks are beautiful ๐Ÿ˜")
// Your ref object will always remain the same after re-renders
// i.e, myRef.current will always be "React hooks are beautiful ๐Ÿ˜" even after when your app re-renders 
}

If you recall earlier, I said useRef returns a mutable object, the question is "How do we mutate the value of the current property?". Let's make some changes to our code above;

const myRefExample =()=>{
const myRef = useRef("React hooks are beautiful ๐Ÿ˜")
// to mutate the value of the current property of myRef object we will do
myRef.current = "React hooks are not just beautiful, they make your code looks pretty ๐Ÿค—"

//now we've updated the value of the current property of myRef object to a new text. myRef object will now always be {current: "React hooks are not just beautiful, they make your code looks pretty ๐Ÿค—"} 
}

Familiar use cases of useRef ๐Ÿ’ผ

I am a big fan of learning things by understanding situations and use cases where it has been used before. So I decided to ask a group of my Frontend Engineer friends about situations they've used useRef and ref. Here is a list of their responses.

1. "ref and useRef for me is used to target the HTML element. I once used it to target an audio element and perform some HTML-specific operations on it like changing the src"

import React, {useRef} from "react"

const AudioPlayer =()=>{
    const audioRef = useRef(null);

    const playAudio = () => {
    audioRef.current.play();
      };

      const pauseAudio = () => {
    audioRef.current.pause();
      };

      const skipAudio = () => {
    audioRef.current.currentTime += 10; 
    // Skips audio 10 seconds forward
      };

    return(
    <div>
      <audio ref={audioRef} src="audio.mp3"></audio>
      <button onClick={playAudio}>Play</button>
      <button onClick={pauseAudio}>Pause</button>
      <button onClick={skipAudio}>Skip 10 Seconds</button>
    </div>
    )

}

2. "I use it to target dom elements. I mostly use it when I am trying to work on uploading files or images"

import React, { useRef } from 'react';

const FileUpload=()=>{
  const fileInputRef = useRef(null);

  const handleUpload = () => {
    const file = fileInputRef.current.files[0];
    console.log(file);
    // withe useRef you can now access the current file object
    // code to upload the file goes here in case you wanna upload to a server.
  };

  return (
    <div>
      <input type="file" ref={fileInputRef} />
      <button onClick={handleUpload}>Upload</button>
    </div>
  );
}

3. "I use it to hold generic value"

import React, { useRef } from 'react';

const Counter =()=> {
  // Create two refs using the useRef hook
  const countRef = useRef(0);
  const messageRef = useRef('');

  const handleClick = () => {
     // Increment the countRef value
    countRef.current++;
 // Update the messageRef value based on the current countRef value
    if (countRef.current === 1) {
      messageRef.current = 'You clicked 1 time';
    } else {
      messageRef.current = `You clicked ${countRef.current} times`;
    }
    console.log(messageRef.current);
  };

  return (
    <div>
      <p>{messageRef.current}</p>
      <button onClick={handleClick}>Click me!</button>
    </div>
  );
}

export default Counter;

Common pitfalls to avoid when using useRef and ref ๐Ÿƒ๐Ÿฟ

  1. Mutating the ref object directly: When you create a ref object, you should treat it as a read-only value and avoid mutating it directly. Instead, you should use the .current property of the ref object to read or update its value.

  2. Using useRef to force re-renders: Although useRef can be used to store values you want to persist between renders, it should not be used as a way to force your component to re-render. For this, it is very much advisable to use hooks like useState or useReducer to manage values that affects rendering.

  3. Accessing DOM nodes too early with ref: If you try to access a DOM node using a ref object before the component that contains it has mounted, the ref will be null. To avoid this, you should only access ref objects after the component has mounted.

  4. Creating too many ref objects: Like the saying that says "Too much of everything is bad", the same applies to useRef. Although it is very useful too and provides an escape hatch to the DOM for you, creating too many ref objects however can make your code very ambiguous, and hard to understand and maintain. useRef should only be used when necessary and should be kept to a minimum. You can read up on how to keep your code easy to understand and less ambiguous here.

Conclusion ๐Ÿ

I hope this article helps you understand how useRef works and how to apply it in your React applications. Go forth and utilize the power useRef provides in your projects, and remember "With great power comes great responsibility".

Adios! ๐Ÿ‘‹