Skip to main content

useRef Hook in React

useRef

useRef is a hook in React that provides a way to maintain persistent values between renders without causing a re-render. It creates a mutable object with a .current property that persists across component renders. Changes to .current do not trigger re-renders.

const ref = useRef(initialValue);
  • ref.current holds the current value of the ref.

  • initialValue is the starting value of the ref.

It’s often used for directly accessing or manipulating DOM elements and for storing mutable values like timers or counters.


Key Use Cases Of useRef


1. Accessing DOM Elements

useRef can be used to directly reference a DOM element and perform operations like focusing, scrolling, or manipulating its properties.

import React, { useRef } from "react";

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

const handleFocus = () => {
// inputRef.current will reference the actual <input> DOM element.
inputRef.current.focus(); // Focuses the input element
};

return (
<div>
<input type="text" ref={inputRef} placeholder="Click the button to focus" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}

export default InputFocus;

2. Preserving State Between Renders

Sometimes, you may need to store values like timers or flags that need to persist across renders but should not trigger a re-render when they change. timerRef stores the interval ID, avoiding unnecessary re-renders caused by state updates.

import React, { useRef, useState } from "react";

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

const startTimer = () => {
if (!timerRef.current) {
timerRef.current = setInterval(() => setTime((prev) => prev + 1), 1000);
}
};

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

return (
<div>
<p>Time: {time}s</p>
<button onClick={startTimer}>Start</button>
<button onClick={stopTimer}>Stop</button>
</div>
);
}

export default Timer;

Avoid Reading Or Writing ref.current During Rendering

React expects components to act as pure functions during rendering, producing consistent output without side effects. Reading or writing ref.current during rendering violates this principle and can lead to unpredictable behavior.

function MyComponent() {
// ...
// 🚩 Don't write a ref during rendering
myRef.current = 123;
// ...
// 🚩 Don't read a ref during rendering
return <h1>{myOtherRef.current}</h1>;
}

You can read or write refs from event handlers or effects instead.

function MyComponent() {
// ...
useEffect(() => {
// ✅ You can read or write refs in effects
myRef.current = 123;
});
// ...
function handleClick() {
// ✅ You can read or write refs in event handlers
doSomething(myOtherRef.current);
}
// ...
}

Ref Callback

A ref callback is a function you pass to the ref attribute of a JSX element. React calls this function with the DOM element when it mounts or updates and with null when it unmounts.


Basic Usage

import { useState, useRef } from "react";

export default function App() {
const [numberVal, setNumberVal] = useState(0);

const setValueRef = (node) => {
if (node) {
node.value = numberVal;
node.focus();
}
};

const setNumberHandler = () => {
setNumberVal(Math.random());
};

return (
<div>
<input ref={setValueRef} />
<button onClick={setNumberHandler}>Generate Number</button>
</div>
);
}

Using Ref Callback For Cleanup

Ref callbacks are handy for attaching and detaching event listeners or performing resource cleanup.

function EventListenerExample() {
const refCallback = (node) => {
if (node) {
// Attach event listener when ref is set
node.addEventListener("click", handleClick);
} else {
// Cleanup when ref is unset
node?.removeEventListener("click", handleClick);
}
};

const handleClick = () => {
console.log("Element clicked!");
};

return <div ref={refCallback}>Click Me!</div>;
}

When to Use Ref Callbacks Over useRef

  • You need logic to run whenever a ref is set or cleared.
  • You want to handle resource cleanup automatically.

Forwarding Refs

Forwarding refs is a technique in React that allows you to pass a ref from a parent component down to a child component, giving the parent access to the child’s DOM node or instance. This is done using the React.forwardRef function.

import React, { forwardRef } from "react";

export default function App() {
const inputRef = React.useRef();

const focusInput = () => {
inputRef.current.focus(); // Focuses the input element in FancyInput
};

return (
<div>
<FancyInput ref={inputRef} placeholder="Forwarding Ref Example" />
<button onClick={focusInput}>Focus Input</button>
</div>
);
}

const FancyInput = forwardRef((props, ref) => {
return <input ref={ref} {...props} />;
});

In the above example,

  1. FancyInput uses React.forwardRef to allow a ref to be passed down to the <input>.
  2. The parent component (App) accesses the <input> DOM node using inputRef.

useImperativeHandle

The useImperativeHandle hook customizes the instance value that is exposed when using ref. It allows you to define specific methods or properties the parent can access, rather than exposing the entire DOM element.

import React, { useRef, forwardRef, useImperativeHandle } from "react";

export default function App() {
const inputRef = useRef();

const handleFocus = () => inputRef.current.focus();
const handleClear = () => inputRef.current.clear();

return (
<div>
<CustomInput ref={inputRef} placeholder="UseImperativeHandle Example" />
<button onClick={handleFocus}>Focus Input</button>
<button onClick={handleClear}>Clear Input</button>
</div>
);
}

const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef();

// Expose specific methods to the parent
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
clear: () => {
inputRef.current.value = "";
},
}));

return <input ref={inputRef} {...props} />;
});

In the above example,

  • Inside CustomInput, useImperativeHandle is used to expose custom methods (focus and clear) to the parent.
  • These methods are attached to the ref passed down from the parent.
  • The parent component (App) calls these methods using the inputRef.

useRef vs useState

AspectRefs (useRef)State (useState)
Return ValueReturns an object with a current property: { current: initialValue }.Returns the current value of the state and a setter function: [value, setValue].
Triggers Re-renderDoesn’t trigger re-render when you change it.Triggers re-render when you change it.
MutabilityMutable — you can modify and update current’s value, and changes are updated synchronously.Immutable — you must use the state setter function to modify state, which queues a re-render.
Reading/Accessing ValueYou shouldn’t read or write the current value during rendering.You can read state at any time, even during rendering.
Common Use Cases- Storing DOM references (e.g., for focus management).
- Maintaining values across renders without triggering re-renders.
- Storing mutable values like timers, previous values, etc.
- Managing UI state (e.g., form inputs, toggles, counters).
- Storing data that needs to trigger re-renders (e.g., fetched data, user actions).

This table highlights the key differences between refs and state in React and provides examples of their typical use cases.