logo
Published on

useref in react

Authors
  • avatar
    Name
    Ganesh Negi
    Twitter

When it comes to React hooks, useRef is often overlooked — but it’s way more versatile than most developers realize.

While it’s commonly used to access and interact with DOM elements, useRef has another super handy use:

it lets you store values that stick around between renders without triggering a re-render.

In this blog, we’ll break down some real-world examples where useRef can make your React apps cleaner, faster, and more efficient.

useref in react

useref in react

The useRef hook in React gives you a special kind of object — a mutable one — with a .current property you can use to hold on to any value.

What makes it different from useState is that changing this value won’t cause your component to re-render.

That means you can update it as often as you like, without triggering any visual updates.

Here’s a simple and practical example:

import React, { useRef } from 'react';

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

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

  return (
    <div>
      <input ref={inputRef} type="text" />
      <button onClick={focusInput}>Focus Input</button>
    </div>
  );
}

Real-Life Examples of useRef in react

useref in react with example

1. Accessing and Controlling DOM Elements with useRef

One of the most common — and practical — uses of useRef is interacting directly with DOM elements. Whether you're working with custom animations, third-party libraries, or just want more control over a component, useRef makes it easy.

Here’s a hands-on example using a video element:

import React, { useRef, useEffect } from 'react';

function VideoPlayer() {
  const videoRef = useRef(null);

  useEffect(() => {
    // Auto-play the video as soon as the component loads
    videoRef.current.play();
  }, []);

  return (
    <div>
      <video ref={videoRef} width="600" controls>
        <source src="video.mp4" type="video/mp4" />
        Your browser doesn't support the video tag.
      </video>
    </div>
  );
}

In this case, useRef is used to grab a reference to the video DOM element. As soon as the component mounts, we tell the video to start playing automatically — no clicks required.

This kind of pattern is super helpful when you need to integrate with features that React doesn’t directly manage, like video APIs, focus handling, scroll positions, or even chart libraries.

2. Keeping Values Between Renders (Without Causing Re-Renders)

Sometimes, you need to keep track of a value between renders — like a render count, previous state, or a timestamp — but you don’t want to trigger a re-render every time that value changes. This is exactly where useRef shines.

for example:-

import React, { useState, useRef, useEffect } from 'react';

function Counter() {
  const [count, setCount] = useState(0);
  const renderCount = useRef(0);

  useEffect(() => {
    renderCount.current++;
  });

  return (
    <div>
      <p>Count: {count}</p>
      <p>This component has re-rendered {renderCount.current} times</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

Here’s what’s happening:

Every time the Counter component re-renders, the useEffect hook fires and bumps up renderCount.current.

Since useRef doesn’t cause re-renders when updated, the UI stays performant — and we still get access to the updated value.

This trick is super useful when you want to monitor side effects, track previous values, or store data that’s only needed internally during the component’s lifecycle.

3. Storing Previous State Values

Ever wanted to compare your current state to what it was just before? React doesn’t give you this out of the box, but useRef makes it easy to track the previous value without triggering re-renders.

for example:

import React, { useState, useEffect, useRef } from 'react';

function PreviousStateExample() {
  const [name, setName] = useState('John');
  const prevNameRef = useRef('');

  useEffect(() => {
    prevNameRef.current = name;
  }, [name]);

  return (
    <div>
      <p>Current Name: {name}</p>
      <p>Previous Name: {prevNameRef.current}</p>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
    </div>
  );
}

What’s happening here:

We use useRef to hold onto the previous value of the name state.

Every time name updates, the useEffect hook stores the current name into the ref — right before the next render.

This pattern is especially helpful when you need to trigger animations, run logic based on changes, or just want to show users how their input is evolving in real-time.

4. Avoiding Re-Initialization of Expensive Calculations (Skipping Recalculation of Expensive Values)

Some operations in your app might be heavy — things like complex math, data parsing, or calling functions that take time or resources. If you don’t want them to run on every render, useRef is a great way to store the result once and reuse it.

import React, { useRef } from 'react';

function ExpensiveCalculationComponent() {
  const expensiveValue = useRef(calculateExpensiveValue());

  function calculateExpensiveValue() {
    console.log('Calculating expensive value...');
    return 42; // Just a placeholder for a real heavy computation
  }

  return (
    <div>
      <p>Expensive Value: {expensiveValue.current}</p>
    </div>
  );
}

here,

The calculateExpensiveValue() function is called once, on the initial render.

The result is stored in the .current property of the ref.

Even if the component re-renders later, the calculation won’t run again — you’re reusing the stored value.

5. Managing setTimeout and setInterval Cleanly

Timers like setTimeout and setInterval are super handy — but they also need to be cleaned up properly to avoid bugs, memory leaks, or unexpected behavior. That’s where useRef can really help by safely storing the timer ID.

Take a look at this example:

import React, { useState, useRef, useEffect } from 'react';

function Timer() {
  const [count, setCount] = useState(0);
  const timerRef = useRef(null);

  useEffect(() => {
    // Start the interval when the component mounts
    timerRef.current = setInterval(() => {
      setCount((prevCount) => prevCount + 1);
    }, 1000);

    // Clear the interval when the component unmounts
    return () => {
      clearInterval(timerRef.current);
    };
  }, []);

  return <p>Count: {count}</p>;
}

Here’s what’s happening:

The interval ID returned by setInterval is stored in timerRef.

When the component unmounts, clearInterval is called using that stored ID to prevent the timer from continuing in the background.

Conclusion (useRef in react)

Conclusion useref in react

The useRef hook might seem simple at first glance, but it's a powerhouse once you know how to use it right.

From DOM manipulation and storing previous state values to handling timers and avoiding unnecessary recalculations.

By putting these real-world examples into practice, you'll not only write more optimized code but also gain better control over your component's behavior without triggering unnecessary re-renders.

So whether you're building a small widget or architecting a large-scale React app, don’t overlook useRef.

Experiment with it, play around, and you'll keep finding clever new ways to use this versatile little hook.