Lesson 5 — useRef Hook (Beyond DOM Access)

🧭 Introduction

Most developers learn useRef like this:

“useRef is used to access DOM elements.”

While that is true, it is also only 20% of the story.

In real-world React applications, useRef is a powerful advanced hook used for:

  • Persisting values without re-rendering
  • Avoiding unnecessary renders
  • Managing timers, intervals, and previous values
  • Solving problems that useState cannot

In this lesson, you’ll learn what useRef really is, how it works, and when to use it correctly.


🎯 What You’ll Learn in This Lesson

By the end of this lesson, you will understand:

  • What useRef actually returns
  • Difference between useRef and useState
  • DOM access use cases
  • Persisting mutable values
  • Tracking previous values
  • Common mistakes and best practices

🧠 What is useRef? (Clear Definition)

useRef is:

A hook that returns a mutable object whose value persists across renders without causing re-renders.

Syntax:

const ref = useRef(initialValue);

It returns:

{ current: initialValue }


🔑 Key Properties of useRef

✅ Value persists between renders
✅ Updating .current does not trigger re-render
✅ Same ref object is returned every render

This makes it very different from useState.


🆚 useRef vs useState (Critical Comparison)

FeatureuseRefuseState
Causes re-render❌ No✅ Yes
Persists value✅ Yes✅ Yes
Mutable✅ Yes❌ No
Triggers UI update❌ No✅ Yes
Best forNon-UI dataUI data

👉 Golden Rule:
If the value affects UI → useState
If it does not → useRef


🧩 Basic DOM Access Example

This is the most common use case.

import { useRef } from "react";

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

  const focusInput = () => {
    inputRef.current.focus();
  };

  return (
    <>
      <input ref={inputRef} />
      <button onClick={focusInput}>Focus</button>
    </>
  );
}

👉 React does not re-render when .focus() is called.


⚠ Important Note About Timing

ref.current is:

  • null on first render
  • Assigned after DOM is mounted

That’s why DOM refs are usually used:

  • Inside event handlers
  • Inside effects

🔁 Persisting Values Across Renders (Real Power)

❌ Wrong Approach with useState

const [timerId, setTimerId] = useState(null);

This causes unnecessary re-renders.

✅ Correct Approach with useRef

const timerId = useRef(null);

Perfect for:

  • Timers
  • Intervals
  • WebSocket connections
  • Previous values

🧪 Example — Interval with useRef

function Timer() {
  const intervalRef = useRef(null);

  const start = () => {
    intervalRef.current = setInterval(() => {
      console.log("Running...");
    }, 1000);
  };

  const stop = () => {
    clearInterval(intervalRef.current);
  };

  return (
    <>
      <button onClick={start}>Start</button>
      <button onClick={stop}>Stop</button>
    </>
  );
}

👉 No re-render needed for interval ID.


🔍 Tracking Previous Value (Advanced Pattern)

React does not give previous state automatically.
useRef solves this cleanly.

function Counter({ count }) {
  const prevCount = useRef(count);

  useEffect(() => {
    prevCount.current = count;
  }, [count]);

  return (
    <p>
      Current: {count}, Previous: {prevCount.current}
    </p>
  );
}

👉 useRef stores previous value without affecting render.


🧠 Why useRef Does NOT Trigger Re-render

React:

  • Tracks state changes for rendering
  • Ignores ref mutations

Because refs are meant for:

“Escape hatches” from React’s rendering cycle


🚨 Common Mistakes with useRef

❌ Mistake 1: Expecting UI to Update

ref.current++;

❌ UI won’t update
✅ Use state for UI data


❌ Mistake 2: Using useRef Instead of State Everywhere

useRef is not a replacement for state.


❌ Mistake 3: Mutating State Instead of Using Ref

state.value = 10; // ❌


🧠 useRef in Real-World Apps

Professional use cases:

  • Form libraries (React Hook Form)
  • Animation libraries
  • Measuring DOM size
  • Avoiding stale closures
  • Performance optimization

🔗 useRef & Closures (Important Insight)

Closures capture values at render time.
Refs always point to latest value.

This makes useRef useful for:

  • Event handlers
  • Async callbacks
  • Intervals

🎯 Best Practices

✅ Use refs for non-visual data
✅ Prefer state for UI updates
✅ Keep ref usage intentional
✅ Don’t abuse refs


❓ FAQs — useRef Hook

🔹 Can useRef replace useState?

No. They solve different problems.


🔹 Does updating ref cause re-render?

No — and that’s the point.


🔹 Is useRef safe?

Yes, when used correctly.


🔹 Why is useRef called an escape hatch?

Because it bypasses React’s render cycle.


🧠 Quick Recap

✔ useRef stores mutable values
✔ Does not cause re-render
✔ Perfect for DOM access & timers
✔ Useful for previous values
✔ Essential advanced hook


🎉 Conclusion

useRef is one of the most misunderstood hooks — yet one of the most powerful.

Once you master it:

  • You’ll write cleaner logic
  • You’ll avoid unnecessary renders
  • You’ll solve problems that state cannot

This lesson completes your understanding of non-rendering state in React ⚛️🧠


👉 Next Lesson

Lesson 6 — forwardRef in React (Real-World Use Cases)

Leave a Comment