← Back to React Course | Chapter 5: Core Hooks | Lesson 6 of 12

useRef Hook

useRef is a sticky note that remembers a value across renders without ever causing the page to redraw when it changes.

Accessing a DOM Element with useRef

Passing a ref object to an element's ref prop gives you direct access to the actual DOM node once it's rendered, available at ref.current. This is useful for things React doesn't have a built-in prop for, like focusing an input programmatically.

Note: ref.current for a DOM element is only populated AFTER the component renders -- access it inside an event handler or useEffect, not during render.

Warning: Reading ref.current directly in the render body (not inside an effect or handler) will be null on the first render, before the DOM exists.

Example: Accessing a DOM Element with useRef

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function FocusInput() {
      const inputRef = React.useRef(null);
      return (
        <div>
          <input ref={inputRef} placeholder="Click button to focus me" />
          <button onClick={() => inputRef.current.focus()}>Focus Input</button>
        </div>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<FocusInput />);
  </script>
</body>
</html>

Storing a Mutable Value Without Re-rendering

Unlike state, updating a ref's .current value does not cause the component to re-render. This makes refs ideal for storing values you need to keep track of, like a timer ID or a previous value, without triggering unnecessary UI updates.

Note: Use a ref (not state) for values that the component needs to remember but that should never, by themselves, cause a re-render.

Warning: If you need the UI to visually update when a value changes, use state instead -- a ref update alone won't do that.

Example: Storing a Mutable Value Without Re-rendering

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function ClickCounter() {
      const clicksRef = React.useRef(0);
      function handleClick() {
        clicksRef.current += 1;
        console.log("Clicked", clicksRef.current, "times (check console, no re-render)");
      }
      return <button onClick={handleClick}>Click me (see console)</button>;
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<ClickCounter />);
  </script>
</body>
</html>

useRef vs useState

Both persist a value across renders, but they serve very different purposes: state changes should be visible in the UI and trigger a re-render, while ref changes are typically 'behind the scenes' bookkeeping that shouldn't affect what's displayed. Choosing the right one avoids both unnecessary re-renders and missing UI updates.

Note: Ask: 'should the screen update when this value changes?' If yes, use state. If no, use a ref.

Warning: Using a ref for a value that should be visible on screen means the UI silently won't update when it changes -- a common source of confusing bugs.

Example: useRef vs useState

markup
<!DOCTYPE html>
<html>
<head>
  <script src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
</head>
<body>
  <div id="root"></div>
  <script type="text/babel">
    function Comparison() {
      const [stateValue, setStateValue] = React.useState(0);
      const refValue = React.useRef(0);
      return (
        <div>
          <p>State (updates UI): {stateValue}</p>
          <button onClick={() => setStateValue(stateValue + 1)}>Update State</button>
          <button onClick={() => { refValue.current += 1; console.log("Ref is now", refValue.current); }}>Update Ref (check console)</button>
        </div>
      );
    }
    ReactDOM.createRoot(document.getElementById('root')).render(<Comparison />);
  </script>
</body>
</html>
Common Mistakes
  1. Expecting a change to a ref's .current value to trigger a re-render -- it never does.
  2. Reading ref.current during the render itself for a DOM element ref, before the DOM has actually been created.
  3. Confusing useRef with useState -- refs are for values that don't need to cause re-renders, state is for values that do.
Chapter Summary
  • useRef returns a mutable object with a single .current property.
  • Changing .current does NOT trigger a re-render, unlike state.
  • A common use is accessing a DOM element directly, via the ref prop.
  • Another common use is storing a mutable value that persists across renders without causing updates.
Browser Support

Available since React 16.8, when Hooks were introduced.

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.