Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 59

Direct DOM Access with useRef

Recommended resources

Hello! In our previous lesson, we combined useReducer and useContext to create a powerful, scalable pattern for managing global state. This is a cornerstone for complex applications like the e-commerce site you're aiming to build.

Today, we shift our focus from state management to a different, but equally important, tool: the useRef hook. While hooks like useState and useReducer are about managing data that causes your component to re-render, useRef gives you a way to "escape" the typical React flow. Its primary purpose, and our focus today, is to get a direct reference to a DOM element, allowing you to interact with it imperatively—for example, to programmatically manage focus.

This is a key skill for creating polished user experiences, like focusing an input field when a form appears or managing video playback.

1. What is useRef?

At its core, useRef returns a mutable object whose .current property is initialized to the value you pass as an argument (useRef(initialValue)). This object persists for the full lifetime of the component.

Crucially, there are two main use cases for useRef:

  1. Storing a mutable value that does not cause a re-render when updated. This is the key difference from useState. If you want to keep track of a value across renders but don't want the component to update every time the value changes, a ref is the perfect tool.
  2. Accessing DOM nodes. This is the most common use case and the focus of our lesson.

Let's start by understanding the first point, as it clarifies the fundamental nature of refs.

Learn useRef in 11 Minutes

This video from Web Dev Simplified first explains the core concept of useRef: it allows you to persist a value between renders without triggering an update. This distinction from useState is fundamental.

Watch from 02:45 to 04:45. Pay close attention to how a ref is created and how its value is accessed and updated via the .current property. Notice that changing the ref's value does not cause the component to re-render, unlike changing state.

As you saw, the ref object is simply a container, { current: ... }, that you can read from and write to. React ensures this container is the same one on every render of your component.


2. The Primary Use Case: Accessing DOM Elements

Now for the main event. In React, you generally describe what the UI should look like declaratively with JSX, and React handles updating the actual DOM. However, sometimes you need to perform an imperative action on a specific DOM element, such as calling its .focus() method or measuring its size. This is where useRef shines.

The process involves three simple steps:

  1. Declare a ref in your component: const myRef = useRef(null);
  2. Attach the ref to a JSX element using the ref attribute: <input ref={myRef} />
  3. Access the DOM node via the ref's .current property, which will now hold the DOM element itself: myRef.current.focus();

React will automatically set the .current property to the corresponding DOM node when the component mounts and set it back to null when it unmounts.

The following video demonstrates this exact flow by building a component that focuses an input field when a button is clicked.

Learn useRef in 11 Minutes

Now, let's see the most common use of useRef in action: getting a direct reference to a DOM element to call one of its methods.

Continue watching from 04:45 to 06:40. The video clearly shows how to create a ref, attach it to an <input> element, and then use that ref in a function to call the input's built-in .focus() method.

Here is a code example summarizing the pattern you just saw:

import React, { useRef } from 'react';

function FocusInput() {
  // 1. Declare a ref to hold the input element
  const inputRef = useRef(null);

  const handleFocusClick = () => {
    // 3. Access the DOM node via .current and call its method
    if (inputRef.current) {
      inputRef.current.focus();
    }
  };

  return (
    <>
      {/* 2. Attach the ref to the JSX element */}
      <input ref={inputRef} type="text" placeholder="Click button to focus me" />
      <button onClick={handleFocusClick}>Focus the Input</button>
    </>
  );
}

This pattern is extremely useful for improving accessibility and user workflow, such as automatically focusing the first field in a login form or a search bar.


3. A Word of Caution: Don't Abuse Refs

Because useRef gives you direct access to the DOM, it can be tempting to use it to manually change things, like an input's value or an element's style. You should avoid this.

React's power comes from its declarative nature and its control over the DOM via state and props. If you start manipulating the DOM manually with refs, you are fighting against the framework. This can lead to unpredictable behavior because React won't be "aware" of the changes you've made.

  • To change what's rendered on the screen, use state.
  • To perform an imperative action on a DOM element (like focusing it), use a ref.

Learn useRef in 11 Minutes

It's critical to understand when not to use refs. This final clip warns against the common mistake of using refs to manually manipulate the DOM, which should be handled by React's state management.

Watch from 06:40 to 07:59. The video explains why doing something like inputRef.current.value = '...'; is a bad practice. The correct way to manage an input's value is with the useState hook, creating a controlled component, which we covered in a previous module.

Think of useRef as a necessary escape hatch for situations where the declarative approach isn't possible, not as a primary tool for building your UI.


Conclusion

You've now added another essential hook to your toolkit. While state management hooks help you control your component's data and rendering, useRef provides a bridge to the browser's DOM for imperative actions.

Key Takeaways:

  • useRef creates a persistent, mutable object with a .current property.
  • Updating a ref's .current value does not trigger a component re-render.
  • The primary use case for useRef is to get a direct reference to a DOM element by passing the ref to the element's ref attribute.
  • Once you have the DOM element in ref.current, you can call its methods imperatively (e.g., ref.current.focus()).
  • Avoid using refs to manipulate the DOM declaratively (e.g., changing values or content). Use state for that.

Preview of the Next Lesson:

We've covered hooks for state (useState, useReducer), context (useContext), and side effects (useEffect). In the next lesson, we'll look at a hook designed for performance optimization: useMemo. You'll learn how to use it to "memoize" the results of expensive calculations so they don't have to be re-run on every render, making your application faster and more efficient.