Understanding useState vs useRef in React

Nov 15, 2024
10min read time
Understanding useState vs useRef in React

React provides powerful tools to manage state and references in functional components. Two commonly used hooks, useState and useRef, often come up in discussions about managing component behavior. While they might seem similar at first glance, they serve different purposes. In this blog, we’ll explore the key differences between useState and useRef and when to use each.

What is useState?

The useState hook is used for managing stateful logic in React components. It allows you to create state variables, which will cause the component to re-render whenever their values change.

Key Characteristics:

  1. Triggers Re-Renders: Any update to a useState variable will re-render the component to reflect the new state.

  2. Asynchronous Updates: State updates are batched and applied asynchronously.

  3. Used for UI State: Ideal for managing state that directly affects what the user sees, such as form inputs, counters, or toggles.

Example

import React, { useState } from 'react';
function Counter() { 
    const [count, setCount] = useState(0);

    const increment = () => {
        setCount(count + 1);
    };

  return (
    <div>
      <p>Current count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

What is useRef?

The useRef hook is primarily used to manage mutable references that do not trigger re-renders. It can store a reference to a DOM element or persist a mutable value across renders.

Key Characteristics:

  1. Does Not Trigger Re-Renders: Updating a useRef value does not cause the component to re-render.

  2. Used for References: Often used to reference DOM elements or store values that need to persist across renders without affecting the UI.

  3. Access to Current Value: The current property is used to access or update the value stored in the reference.

Example

import React, { useRef } from 'react';

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

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

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

Key Differences Between useState and useRef

Feature

useState

useRef

Purpose

Manages state that triggers re-renders

Holds a mutable reference that doesn’t re-render

Triggers Re-Renders

Yes

No

Usage

UI-related state

Accessing or persisting mutable values

Updates

Asynchronous

Synchronous

When to Use useState

Use useState when:

  • You want changes to a value to trigger a re-render of the component.

  • You’re managing user interface states, like form inputs, visibility toggles, or dynamic lists.

  • The value is directly tied to what is displayed in the UI.

Example: Tracking Input State

function TextInput() {
  const [text, setText] = useState('');

  return (
    <div>
      <input 
        value={text} 
        onChange={(e) => setText(e.target.value)} 
        placeholder="Type something" 
      />
      <p>Typed Text: {text}</p>
    </div>
  );
}

When to Use useRef

Use useRef when:

  • You need to reference a DOM element directly.

  • You want to persist values across renders without causing re-renders.

  • You need a mutable variable that React does not track in the rendering cycle.

Example: Tracking Render Counts

function RenderCounter() {
  const renderCount = useRef(0);

  renderCount.current += 1;

  return <p>Component rendered {renderCount.current} times</p>;
}

Using useState and useRef Together

In some cases, you might need both useState and useRef. For example, if you want to manage state that affects the UI while keeping track of a mutable value behind the scenes.

Example: Timer with Pause/Resume

function Timer() {
  const [seconds, setSeconds] = useState(0);
  const timerRef = useRef();

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

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

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

Conclusion

Both useState and useRef are essential hooks in React, each with its distinct purpose. Understanding when to use them will help you manage component behavior more effectively. Use useState for state that impacts the UI and triggers re-renders, and useRef for values that need to persist without causing updates to the UI.

By mastering these hooks, you’ll have the tools to build dynamic and responsive React applications with ease!