DEV Community

Cover image for The React useRef Hook: Not Just for DOM Elements

The React useRef Hook: Not Just for DOM Elements

Nick Taylor on July 08, 2024

In this post, we'll cover what the useRef hook is, some examples of how it can be used, and when it shouldn't be used. What is useRef? ...
Collapse
 
nickytonline profile image
Nick Taylor • Edited

What are some other use cases you've used the useRef hook for?

Thinking emojis floating around

Collapse
 
devbylanre profile image
John Doe 🧑🏾‍💻

I once had some variables causing my hook to re-render infinitely. Fixed it by storing the variables using useRef

Collapse
 
giovannimazzuoccolo profile image
Giovanni Mazzuoccolo

Besides accessing DOM elements, I've primarily used useRef for handling websockets (similar to your case), managing formData values, SVG drawing, and integrating third-party APIs that weren't designed for use with React.

Collapse
 
nickytonline profile image
Nick Taylor • Edited

Nice! Thanks for sharing, Giovanni.

Collapse
 
ashishsimplecoder profile image
Ashish Prajapati

When you want track any kind of value and don't want to trigger rerender of components when that value is updated, that's the perfect use case of useRef.
In simple words when don't want to link the state to the UI.

Collapse
 
nickytonline profile image
Nick Taylor

Jack Nicholson nodding yes

Collapse
 
syeo66 profile image
Red Ochsenbein (he/him)

Basically for any kind of value which should be be stored across but should not trigger a rerender.

Collapse
 
nickytonline profile image
Nick Taylor

Pretty much that. Thanks for giving it a read Red!

Collapse
 
ashishsimplecoder profile image
Ashish Prajapati

Have used it to replace useMemo and useCallback.

Collapse
 
link2twenty profile image
Andrew Bone

Great place to store an abort controller that can be accessed by several functions.

Collapse
 
nickytonline profile image
Nick Taylor

Yeah, that's another great example @link2twenty! Do have any example code lying around to share in a gist or CodeSandbox?

Collapse
 
link2twenty profile image
Andrew Bone • Edited

Here is a quick demo I just threw together. AbortController's are super good at preventing your frontend app spamming endpoints.

codesandbox.io/p/sandbox/abort-con...

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

const dateFormat = Intl.DateTimeFormat("en-GB", {
  year: "numeric",
  month: "numeric",
  day: "numeric",
  hour: "2-digit",
  minute: "2-digit",
  second: "2-digit",
  hour12: false,
  timeZone: "Europe/London",
});

export default function App() {
  const [lastUpdated, setLastUpdated] = useState<number>(Date.now());
  const [buttonPresses, setButtonPresses] = useState<number>(0);
  const [completedRequest, setCompletedRequest] = useState<number>(0);

  const controller = useRef<AbortController | null>(null);

  /**
   * load a mock slow endpoint
   */
  const getData = async () => {
    // abort any outstanding requests
    controller.current?.abort();
    controller.current = new AbortController();

    setButtonPresses((n) => n + 1);

    try {
      await fetch("https://hub.dummyapis.com/delay?seconds=5", {
        signal: controller.current?.signal,
      });

      setCompletedRequest((n) => n + 1);
      setLastUpdated(Date.now());
    } catch (err) {
      console.log(err);
    }
  };

  return (
    <div className="App">
      <p>
        I last got data from the api {dateFormat.format(new Date(lastUpdated))}
        <br />
        You have pressed the 'update now' button {buttonPresses} time
        {buttonPresses !== 1 ? "s" : ""}
        <br />I have visited the endpoint {completedRequest} time
        {completedRequest !== 1 ? "s" : ""}
      </p>
      <button onClick={getData}>Update now</button>
      <button onClick={() => controller.current?.abort()}>
        Cancel request
      </button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
nickytonline profile image
Nick Taylor

Thanks for sharing!

Yes, that's awesome!

Collapse
 
nickytonline profile image
Nick Taylor

Update 2024/07/20:

I updated the code sample below as @jgarplind pointed out to me on DMs over on Bluesky, that firstNameInputRef.current is not necessary in the dependency array and is misleading potentially to devs that you need it. Thanks, Joel!

import { useEffect, useRef } from "react";

export const SomeComponent = () => {
  const firstNameInputRef = useRef<HTMLInputElement>(null);

  // for plain JavaScript change the above line to
  // const firstNameInputRef = useRef(null);

  useEffect(() => {
    firstNameInputRef.current?.focus();
-  }, [firstNameInputRef.current]);
+  }, []);

  return (
    <form>
      <label>
        First Name:
        <input type="text" ref={firstNameInputRef}/>
      </label>
    </form>
  );
}
Enter fullscreen mode Exit fullscreen mode
Collapse
 
praveen_kumargovindaraj_ profile image
Praveen Kumar Govindaraj

Informative

Collapse
 
nickytonline profile image
Nick Taylor

Glad you found it informative! The More You Know NBC TV campaign video capture

Collapse
 
shaogat_alam_1e055e90254d profile image
Info Comment hidden by post author - thread only accessible via permalink
Shaogat Alam

Interesting topic! Everything is explained articulately and clearly. For your project, consider checking out this free npm package: select-paginated.

Some comments have been hidden by the post's author - find out more