Mahamudul Hasan Rubel
HomeAboutProjectsSkillsExperienceBlogPhotosContact
Mahamudul Hasan Rubel

Senior Software Engineer crafting high-performance web applications and SaaS platforms.

Navigation

  • Home
  • About
  • Projects
  • Skills
  • Experience
  • Blog
  • Photos
  • Contact

Get in Touch

Available for senior/lead roles and consulting.

bd.mhrubel@gmail.comHire Me

© 2026 Mahamudul Hasan Rubel. All rights reserved.

Built with using Next.js 16 & Tailwind v4

Back to Blog
ReactJune 21, 20264 min read

React useRef Hook: Mastering DOM Access and Mutable State

React useRef is your escape hatch for DOM manipulation and persistent mutable state. Learn how to use it effectively without breaking your component logic.

reactjavascriptfrontend developmentreact hooks tutorialreact useRefdom manipulationweb performanceNext.jsTutorial

I remember the first time I tried to manually focus an input field in a React component. I instinctively reached for document.getElementById, only to have my senior lead point out that I was fighting the framework, not working with it. That was my introduction to the useRef mental model, and it's a lesson that still saves me about two hours of debugging every time I start a new project.

If you’ve already mastered useState and useEffect: A Mental Model for React Beginners, you know that React is designed to keep your UI in sync with your data. When state changes, the component re-renders. But sometimes, you need to step outside that loop. That’s where useRef comes in.

Understanding the React useRef Hook

At its core, react useRef is a plain JavaScript object with a single property: { current: initialValue }. Unlike useState, updating this .current value does not trigger a re-render. It’s a persistent container that survives the entire lifecycle of your component.

Think of it as a "side pocket" for your component. You can put things in it, take them out, or change them whenever you want, but React won’t notice and won’t update the screen. This makes it perfect for two specific scenarios:

  1. Direct DOM Access: Interacting with native browser APIs.
  2. Mutable Persistence: Storing values (like timers or previous state) that shouldn’t trigger a UI update.

Using Refs for DOM Manipulation

When you need to measure an element, focus an input, or integrate with a third-party library like D3.js or Google Maps, you need direct access to the DOM.

JAVASCRIPT
import { useRef } from CE9178">'react';

function FocusInput() {
  const inputRef = useRef(null);

  const handleClick = () => {
    // Access the DOM node directly via .current
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={inputRef} type="text" />
      <button onClick={handleClick}>Focus the input</button>
    </>
  );
}

In this example, we pass the inputRef to the ref attribute of the input element. React automatically assigns the underlying DOM node to inputRef.current after the component mounts. It’s clean, predictable, and doesn't involve any "dirty" global document queries.

Mutable State Without Re-renders

Sometimes, you need to track something—like a setInterval ID or the previous value of a prop—that doesn't need to be displayed in your JSX. Using useState here would be a mistake because every update would cause an unnecessary render cycle.

We once spent about 45 minutes tracking down a performance bottleneck in a dashboard that was re-rendering 60 times per second because we were storing a mouse-position variable in useState. Moving that to useRef immediately solved the jank.

JAVASCRIPT
function Timer() {
  const timerRef = useRef(null);

  const startTimer = () => {
    timerRef.current = setInterval(() => {
      console.log(CE9178">'Tick');
    }, 1000);
  };

  const stopTimer = () => {
    clearInterval(timerRef.current);
  };

  return (
    <div>
      <button onClick={startTimer}>Start</button>
      <button onClick={stopTimer}>Stop</button>
    </div>
  );
}

The "Wrong Turn" Trap

A common mistake I see junior developers make is using useRef to store data that should be reflected in the UI. If you find yourself writing ref.current = value and then manually updating an element's text content, you are fighting React.

If the user needs to see the change, use useState. If you’re just tracking internal metadata, use useRef. If you're ever in doubt, ask yourself: "Does the user need to see this value change?" If the answer is yes, useRef is likely the wrong tool.

Integrating with the Render Cycle

It’s important to remember that useRef is not reactive. If you read ref.current during the render phase (the main body of your component), you might get stale or inconsistent values.

As I discuss in React Rendering: How State Updates and Reconciliation Work, React expects your render function to be pure. Accessing or modifying a ref during render can lead to unpredictable behavior because you're introducing side effects into a process that should be deterministic.

Always read or write to refs inside useEffect or event handlers. This ensures you're interacting with the DOM or your mutable data only after React has finished its reconciliation process.

Final Thoughts

The useRef hook is a powerful escape hatch, but like any escape hatch, you should only use it when necessary. Most of the time, your state should live in useState or be lifted up to a parent component.

I still occasionally catch myself trying to solve a complex state issue with refs, only to realize I'm overcomplicating things. When that happens, I delete the ref, step back, and rethink the component structure. Are you still unsure if you need a ref? Start with useState. If your component starts lagging or you find yourself doing weird hacks to prevent re-renders, that’s your signal to move that specific piece of data into useRef.

Back to Blog

Similar Posts

Close-up of HTML and JavaScript code on a computer screen in Visual Studio Code.
ReactNext.jsJune 21, 20264 min read

React composition and the children prop for scalable UI libraries

React composition and the children prop are essential for building reusable components. Learn to avoid prop drilling and create flexible, declarative UIs.

Read more
React
Next.js
June 21, 2026
5 min read

React Conditional Rendering: Mastering Guard Clauses and Ternary Chains

React conditional rendering shouldn't be a mess of nested ternaries. Learn how to use guard clauses and declarative patterns to keep your UI logic clean.

Read more
ReactNext.jsJune 21, 20264 min read

React State Synchronization: How to Avoid Infinite Loops

Master React state synchronization by avoiding unnecessary useEffect calls. Learn to handle dependent inputs correctly and keep your components predictable.

Read more