React useRef Hook

The useRef hook provides a way to directly access and interact with a DOM element or persist a mutable value across renders without causing a re-render. It is particularly useful for scenarios like focusing an input field, accessing DOM elements, or storing references to values that do not trigger a component update.

Let us go through some examples to understand the usage of useRef Hook.


1. Example: Reading Input Value

In this example, we use useRef to create a reference to an input element. The reference is then used to read the value of the input field and display it when a button is clicked. This demonstrates how useRef interacts with the DOM without causing re-renders.

App.js

</>
Copy
import React, { useRef, useState } from 'react';

function ReadInput() {
  const inputRef = useRef(); // Create a reference to the input element
  const [inputValue, setInputValue] = useState('');

  const readValue = () => {
    setInputValue(inputRef.current.value); // Read the input value
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="Type something" />
      <button onClick={readValue}>Read Input</button>
      <p>You entered: {inputValue}</p>
    </div>
  );
}

export default ReadInput;

Explanation:

  • useRef: Creates a reference to the input element, stored in inputRef.
  • inputRef.current.value: Reads the current value of the input field.
  • setInputValue: Updates the state with the input value, which is displayed below the input field.

Output:


2. Example: Storing Mutable Values

Here, useRef is used to store a mutable value that does not trigger a re-render. We will create a counter that tracks how many times a button is clicked, but the counter will not cause the component to re-render.

App.js

</>
Copy
import React, { useRef, useState } from 'react';

function ClickCounter() {
  const countRef = useRef(0); // Mutable reference to store the count
  const [renderCount, setRenderCount] = useState(0);

  const handleClick = () => {
    countRef.current += 1; // Increment the reference value
    console.log(`Button clicked ${countRef.current} times`);
  };

  const forceRender = () => {
    setRenderCount(renderCount + 1); // Force re-render
  };

  return (
    <div>
      <button onClick={handleClick}>Click Me</button>
      <button onClick={forceRender}>Force Re-render</button>
      <p>Mutable count (does not trigger re-render): {countRef.current}</p>
      <p>Render count: {renderCount}</p>
    </div>
  );
}

export default ClickCounter;

Explanation:

  • useRef: Stores a mutable counter value that persists across renders without causing re-renders.
  • countRef.current: Holds the counter value, which updates without triggering a re-render.
  • The forceRender button demonstrates the difference between updating state and updating a reference.

Output:


3. Example: Tracking Previous State

In this example, useRef is used to store the previous state of a component. This can be useful when you need to compare the current state with the previous one.

App.js

</>
Copy
import React, { useState, useEffect, useRef } from 'react';

function PreviousStateTracker() {
  const [value, setValue] = useState('');
  const previousValue = useRef('');

  useEffect(() => {
    previousValue.current = value; // Update the ref with the latest state
  }, [value]); // Runs whenever 'value' changes

  return (
    <div>
      <input
        type="text"
        value={value}
        onChange={(e) => setValue(e.target.value)}
        placeholder="Type something"
      />
      <p>Current Value: {value}</p>
      <p>Previous Value: {previousValue.current}</p>
    </div>
  );
}

export default PreviousStateTracker;

Explanation:

  • useRef: Stores the previous state value in previousValue.
  • The useEffect hook updates the reference whenever the value state changes.
  • This approach allows you to track and display the current and previous states without additional re-renders.

Output:


4. Example: Storing a DOM Timer Reference using useRef Hook

This example demonstrates how to use useRef to store a reference to a DOM timer, allowing you to start and stop the timer without re-creating it on every render.

App.js

</>
Copy
import React, { useState, useRef } from 'react';

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

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

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

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

export default Timer;

Explanation:

  • useRef: Stores the timer ID returned by setInterval.
  • startTimer: Starts the timer and ensures only one timer is active at a time.
  • stopTimer: Clears the timer and resets the reference to null.

Output:


Conclusion

The useRef hook is a versatile tool in React. Whether you’re interacting with DOM elements, storing mutable values, tracking previous state, or managing external resources like timers, useRef provides an efficient way to handle these tasks without triggering unnecessary re-renders.