DEV Community

Cover image for Unlocking the Power of useRef: A Comprehensive Guide for React Developers
Srijan Karki
Srijan Karki

Posted on

Unlocking the Power of useRef: A Comprehensive Guide for React Developers

Unfortunately, useRef is underrated. It is not among the most popular hooks, but it is beneficial. Knowing how and where to use it can achieve great results.

React useRef hook

Let’s Start with the Basics

useRef is a React Hook that lets you reference a value not needed for rendering.

React will remember the value you create through useRef, whether you are making a JavaScript object referencing a node in the DOM or a simple value, and it will not be lost during re-renders.

What Does it Give Us?

  1. Accessing DOM Elements:

    • You can easily reach elements in the DOM. For example, you can get an input field’s value, focus on specific elements, get their height and width, scroll to a particular part of the screen, and more.
  2. Storing Mutable Values:

    • You can remember any data you need without re-rendering components. For example, if you need a counter or a timer, choose useRef over useState.

Examples

Here are some examples to illustrate the power of useRef.

Example 1: Reference to a Number

import React, { useRef } from 'react';

const Counter = () => {
  const refCount = useRef(0);
  const refInputField = useRef(null);

  const onClick = () => {
    refCount.current = refCount.current + 1;
    refInputField.current.focus();
  }

  return (
    <>
      <button onClick={onClick}>
        Click me!
      </button>
      <input ref={refInputField} />
    </>
  );
};

export default Counter;
Enter fullscreen mode Exit fullscreen mode

In this example:

  • refCount is a mutable reference to a number.
  • refInputField is a reference to an input element.
  • When the button is clicked, the counter increments, and the input field gains focus.

Example 2: Keeping Track of Previous Values

Another common use case for useRef is keeping track of previous values.

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

const PreviousValue = () => {
  const [count, setCount] = useState(0);
  const prevCountRef = useRef();

  useEffect(() => {
    prevCountRef.current = count;
  }, [count]);

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

export default PreviousValue;
Enter fullscreen mode Exit fullscreen mode

In this example:

  • prevCountRef keeps track of the previous value of count.
  • The useEffect hook updates prevCountRef.current whenever count changes.
  • This allows you to display both the current and previous counts without triggering unnecessary re-renders.

Advanced Tips and Tricks with useRef

1. Persisting Values Across Renders

useRef can be used to persist values across renders without causing a re-render, unlike useState. This is particularly useful for storing values that don't directly impact the UI but need to be remembered.

Example: Tracking the render count of a component.

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

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

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

  return (
    <div>
      <p>This component has rendered {renderCount.current} times</p>
    </div>
  );
};

export default RenderCounter;
Enter fullscreen mode Exit fullscreen mode

2. Integrating with Third-Party Libraries

useRef is invaluable when working with third-party libraries that require direct manipulation of DOM elements, such as integrating with charting libraries, managing video players, or handling animations.

Example: Integrating a chart library.

import React, { useRef, useEffect } from 'react';
import Chart from 'chart.js/auto';

const ChartComponent = () => {
  const chartRef = useRef(null);

  useEffect(() => {
    const ctx = chartRef.current.getContext('2d');
    new Chart(ctx, {
      type: 'line',
      data: {
        labels: ['January', 'February', 'March', 'April'],
        datasets: [{
          label: 'Sales',
          data: [65, 59, 80, 81],
        }],
      },
    });
  }, []);

  return <canvas ref={chartRef}></canvas>;
};

export default ChartComponent;
Enter fullscreen mode Exit fullscreen mode

3. Avoiding Unnecessary Re-Renders in Complex Applications

In complex applications where performance is critical, using useRef to store mutable objects can help avoid unnecessary re-renders.

Example: Storing a mutable state object.

import React, { useRef } from 'react';

const MutableState = () => {
  const state = useRef({
    name: 'John Doe',
    age: 30,
  });

  const updateName = (newName) => {
    state.current.name = newName;
    console.log('Name updated:', state.current.name);
  };

  return (
    <div>
      <button onClick={() => updateName('Jane Doe')}>
        Update Name
      </button>
    </div>
  );
};

export default MutableState;
Enter fullscreen mode Exit fullscreen mode

4. Avoiding Closure Issues

Using useRef can help avoid closure issues by providing a stable reference to a value that persists across renders.

Example: Timer with useRef to avoid stale state.

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

const Timer = () => {
  const [count, setCount] = useState(0);
  const countRef = useRef(count);
  countRef.current = count;

  useEffect(() => {
    const intervalId = setInterval(() => {
      setCount(countRef.current + 1);
    }, 1000);
    return () => clearInterval(intervalId);
  }, []);

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

export default Timer;
Enter fullscreen mode Exit fullscreen mode

Conclusion

Hooks are great, and you should use them. You can achieve a lot if you understand how React works and apply hooks correctly. useRef is particularly powerful for:

  • Accessing and manipulating DOM elements.
  • Storing mutable values that do not trigger re-renders.
  • Persisting values across renders.
  • Integrating with third-party libraries.
  • Avoiding unnecessary re-renders in complex applications.
  • Mitigating closure issues.

By understanding and utilizing useRef, you can write more efficient and effective React components. The true power of hooks lies in understanding their behavior and applying them judiciously.

Do you know, useState is not always the correct answer?

Top comments (0)