Skip to Content
React in a Week
Day 4 · State & escape hatches

Referencing values with refs

Use useRef to remember values without re-rendering and to reach real DOM elements.

So far everything in your components has been driven by state: change state, React re-renders, the screen updates. But sometimes you need to remember something that has nothing to do with what is on screen, such as a timer ID. And sometimes you need to reach the real browser element, for example to put the cursor in a text box.

For both jobs React gives you an escape hatch: a ref. Escape hatches let you step outside React's normal data flow. Use them sparingly, but know they are there.

A value that doesn't trigger a render

Call useRef with an initial value. It returns an object with a single property, current:

import { useRef } from 'react';

const ref = useRef(0); // ref is { current: 0 }

You can read and change ref.current whenever you like. React keeps the same object between renders, but changing it does not re-render your component.

Live code· A ref counter

The count goes up every click, yet the component never re-renders. That makes refs a poor fit for anything you display. If you want the number shown on the button, that is state.

useStateuseRef
Returns[value, setValue]{ current: value }
Changing it re-rendersYesNo
Read during renderYesNo (except the first time you set it up)
Good forAnything shown on screenTimer IDs, DOM elements, values used only in event handlers

A real use: a stopwatch

A stopwatch needs setInterval, which returns an ID. To stop it later you call clearInterval(id), so you must remember the ID. It is never shown on screen, so it belongs in a ref. The elapsed time is shown, so it belongs in state.

Live code· Stopwatch

Refs to DOM elements

The most common use of refs is to reach a real DOM node. Pass your ref object to the ref attribute of a JSX tag. After React puts that element on the page, it sets ref.current to the DOM node, so you can call browser methods like focus() or scrollIntoView().

Live code· Focus an input

Here is the order of events. On the first render inputRef.current is null, because the input doesn't exist yet. Then React creates the <input> and sets inputRef.current to it. By the time the user clicks the button, the ref points at the input.

Passing a ref to your own component

What if the <input> lives inside a component you wrote, like <SearchInput />? In React 19 you pass ref like any other prop, and the child forwards it to the element it renders.

Live code· ref as a prop
Deep diveWhat about forwardRef?

Older tutorials wrap components in forwardRef(...) to accept a ref. Before React 19, ref was not passed through as a normal prop, so that wrapper was required. Since React 19 you can read ref straight from props, as above, and forwardRef is no longer needed for function components. You will still see it in older code, so it is good to recognize.

Quick check

You want to show how many times a button has been clicked, and update the number on screen. What should you use?

Try it

Focus the title after adding a book

When you add a book, the cursor should jump back to the title input so you can type the next one straight away. Use a ref.

Live code· Challenge: focus after submit

Click the Add button with your mouse and notice the cursor stays on the button.

Show solution
import { useRef, useState } from 'react';

export default function AddBookForm() {
  const [title, setTitle] = useState('');
  const [titles, setTitles] = useState([]);
  const inputRef = useRef(null);

  function handleSubmit(e) {
    e.preventDefault();
    setTitles([...titles, title]);
    setTitle('');
    inputRef.current.focus();
  }

  return (
    <>
      <form onSubmit={handleSubmit}>
        <input
          ref={inputRef}
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="Title"
        />{' '}
        <button type="button" onClick={handleSubmit} disabled={!title}>
          Add
        </button>
      </form>
      <ul>
        {titles.map((t, i) => (
          <li key={i}>{t}</li>
        ))}
      </ul>
    </>
  );
}

Focusing an input is a browser action that React doesn't manage, so doing it from an event handler with a ref is exactly right.

Day 4 progress

0/10

On this page