UseRef in Practice

Introduction

React provides a declarative way to build user interfaces. State and props are the core tools for handling dynamic data, but sometimes you need to interact directly with DOM elements or persist values across renders without triggering re-renders. This is where the useRef hook comes in.

The useRef hook is a versatile tool in React functional components. It can:

  1. Access and manipulate DOM elements.
  2. Persist values across renders without causing a re-render.
  3. Store previous values of state for comparison.

In this post, we will explore practical examples of using useRef for common use cases, including focusing input fields, storing previous state, and working with persistent values.


What is useRef?

useRef is a React hook that returns a mutable ref object. The object has a current property that can hold any value. Unlike state, changing a ref does not trigger a component re-render.

Syntax

const refContainer = useRef(initialValue);
  • refContainer: The ref object.
  • refContainer.current: Stores the mutable value.
  • initialValue: Optional initial value for the ref.

1. Accessing DOM Elements

One of the most common use cases for useRef is to access DOM elements directly. This is useful for focusing inputs, scrolling elements, or triggering animations.

Example: Focusing an Input Field

import React, { useRef } from "react";

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

  const handleFocus = () => {
inputRef.current.focus();
}; return (
<div>
  <input ref={inputRef} type="text" placeholder="Enter your name" />
  <button onClick={handleFocus}>Focus Input</button>
</div>
); } export default FocusInput;

Explanation:

  • inputRef is attached to the input element via ref={inputRef}.
  • Clicking the button triggers inputRef.current.focus(), focusing the input field.
  • This allows direct DOM manipulation in a controlled React environment.

Example: Scrolling to an Element

function ScrollToSection() {
  const sectionRef = useRef(null);

  const scrollToSection = () => {
sectionRef.current.scrollIntoView({ behavior: "smooth" });
}; return (
<div>
  <button onClick={scrollToSection}>Go to Section</button>
  <div style={{ height: "500px" }}></div>
  <div ref={sectionRef} style={{ height: "200px", background: "lightblue" }}>
    Scroll Target
  </div>
</div>
); }
  • Ref can be used to scroll elements into view, ideal for smooth navigation.

2. Persisting Values Across Renders

Unlike state, updating a useRef value does not trigger a re-render. This makes it suitable for storing values that persist across renders but do not need to update the UI.

Example: Storing Interval IDs

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

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

  const startTimer = () => {
intervalRef.current = setInterval(() => {
  setCount((prev) => prev + 1);
}, 1000);
}; const stopTimer = () => {
clearInterval(intervalRef.current);
}; return (
<div>
  <h1>{count}</h1>
  <button onClick={startTimer}>Start</button>
  <button onClick={stopTimer}>Stop</button>
</div>
); } export default Timer;

Explanation:

  • intervalRef stores the interval ID.
  • Since intervalRef.current does not cause re-renders, it is safe to use for mutable values.

3. Example: Focusing Input Fields

Focusing input fields is a practical example often required in forms, search bars, and login screens.

function LoginForm() {
  const usernameRef = useRef();
  const passwordRef = useRef();

  const handleSubmit = (e) => {
e.preventDefault();
console.log("Username:", usernameRef.current.value);
console.log("Password:", passwordRef.current.value);
usernameRef.current.value = "";
passwordRef.current.value = "";
usernameRef.current.focus();
}; return (
<form onSubmit={handleSubmit}>
  <input ref={usernameRef} type="text" placeholder="Username" />
  <input ref={passwordRef} type="password" placeholder="Password" />
  <button type="submit">Login</button>
</form>
); }
  • Refs allow direct access to input values without binding state.
  • Input is reset and focus is returned to the first field after submission.

4. Example: Storing Previous State

Sometimes it is useful to compare current state with previous state. useRef can store previous values without triggering re-renders.

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

function PreviousStateExample() {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
prevCountRef.current = count;
}, [count]); const prevCount = prevCountRef.current; return (
<div>
  <h1>Current Count: {count}</h1>
  <h2>Previous Count: {prevCount}</h2>
  <button onClick={() => setCount(count + 1)}>Increment</button>
</div>
); }

Explanation:

  • prevCountRef.current holds the previous value of count.
  • useEffect updates the ref after each render.
  • Ref persists the value across renders without causing additional re-renders.

5. Combining useRef with useEffect

useRef is often combined with useEffect for advanced use cases such as measuring DOM elements, triggering animations, or detecting first render.

Example: Detecting First Render

function FirstRenderExample() {
  const isFirstRender = useRef(true);

  useEffect(() => {
if (isFirstRender.current) {
  console.log("This is the first render");
  isFirstRender.current = false;
} else {
  console.log("This is a re-render");
}
}); return <div>Hello World</div>; }
  • isFirstRender.current allows you to distinguish between the initial render and subsequent updates.

6. Example: Measuring DOM Elements

function MeasureDiv() {
  const divRef = useRef();

  const handleClick = () => {
const height = divRef.current.clientHeight;
console.log("Div height:", height);
}; return (
&lt;div&gt;
  &lt;div ref={divRef} style={{ height: "150px", background: "lightgreen" }}&gt;
    Measure Me
  &lt;/div&gt;
  &lt;button onClick={handleClick}&gt;Get Height&lt;/button&gt;
&lt;/div&gt;
); }
  • useRef stores a reference to a DOM element and allows measuring dimensions or positions.

7. useRef vs useState

FeatureuseRefuseState
Triggers re-render?NoYes
Stores mutable valueYesNo
Common use casesDOM refs, persistent values, timersComponent state, UI updates
Update patternDirectly modify ref.currentUse setState function
  • Use useRef when you need a mutable object that does not require re-rendering.
  • Use useState when updating a value should cause a re-render.

Best Practices

  1. Use refs for DOM manipulation or non-UI state.
  2. Avoid overusing refs; prefer declarative state when possible.
  3. Do not mutate state with refs; use refs for values that do not need re-rendering.
  4. Combine refs with hooks like useEffect for lifecycle-aware operations.
  5. Name refs clearly to indicate their purpose (inputRef, timerRef, prevStateRef).

Real-World Scenarios

  1. Form Input Autofocus – Automatically focus input fields for better UX.
  2. Timers and Intervals – Store interval IDs without causing unnecessary re-renders.
  3. Animations – Manipulate DOM elements for animations directly.
  4. Storing Previous State – Compare previous and current state in forms, counters, or analytics.
  5. Measuring DOM Elements – Get dimensions for dynamic layouts.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *