DEV Community

Cover image for Mastering 'useRef' in React with TypeScript: 4 Different Use-Cases for 'useRef'
Kirubel Kinfe
Kirubel Kinfe

Posted on • Updated on

Mastering 'useRef' in React with TypeScript: 4 Different Use-Cases for 'useRef'

In React, useRef is a versatile hook that allows developers to interact with the DOM and manage mutable values without triggering re-renders. When combined with TypeScript, it becomes even more powerful, providing type safety and preventing common runtime errors. In this article, we will explore the various use cases of useRef in React, with a focus on using it effectively in TypeScript projects.

Understanding useRef
The useRef hook in React provides access to a mutable object known as a "ref." This ref can hold a reference to a DOM element or any other value and persists across renders. Unlike state variables, changing the value of a ref does not trigger a re-render of the component, making it ideal for certain scenarios.

Basic Usage
Here's how you can use useRef in a functional component:

import React, { useRef } from 'react';

function MyComponent() {
  const myRef = useRef(null);

  // Accessing the current value of the ref
  console.log(myRef.current);

  return <div ref={myRef}>Hello, useRef!</div>;
}
Enter fullscreen mode Exit fullscreen mode

In the above example, we create a ref called myRef and attach it to a DOM element using the ref attribute. You can access the current value of the ref using myRef.current.

Use Cases for useRef in React and TypeScript
1. Accessing DOM Elements
One of the most common use cases for useRef is to gain direct access to DOM elements. This is useful for tasks like focusing an input field or measuring the dimensions of an element:

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

function AutoFocusInput() {
  const inputRef = useRef<HTMLInputElement | null>(null);

  useEffect(() => {
    inputRef.current?.focus();
  }, []);

  return <input ref={inputRef} />;
}
Enter fullscreen mode Exit fullscreen mode

In this example, inputRef allows us to focus the input element when the component mounts, without needing a state variable to trigger a re-render.

2. Storing Previous Values
useRef can be used to store and compare previous values across renders. This is useful for detecting changes in props or state:

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

function ValueChangeDetector(props: { value: number }) {
  const prevValueRef = useRef<number | undefined>();

  useEffect(() => {
    if (prevValueRef.current !== undefined && prevValueRef.current !== props.value) {
      console.log('Value changed:', prevValueRef.current, '->', props.value);
    }

    prevValueRef.current = props.value;
  }, [props.value]);

  return <div>{props.value}</div>;
}
Enter fullscreen mode Exit fullscreen mode

Here, prevValueRef stores the previous value of the value prop, allowing us to compare it and take action when it changes.

3. Storing Unmanaged Values
useRef can be used to store values that do not trigger re-renders when they change. This is useful for caching expensive calculations or values that are not part of the component's state:

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

function ExpensiveComponent() {
  const expensiveValueRef = useRef<number>(0);
  const [count, setCount] = useState<number>(0);

  const calculateExpensiveValue = () => {
    if (expensiveValueRef.current === 0) {
      // Perform expensive calculation
      expensiveValueRef.current = /* ... */;
    }
    return expensiveValueRef.current;
  };

  return (
    <div>
      <p>Expensive Value: {calculateExpensiveValue()}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this example, expensiveValueRef stores the result of an expensive calculation, ensuring that the calculation only occurs once.

4. Interacting with Third-Party Libraries
When integrating React with third-party libraries or APIs that rely on mutable values, useRef can be used to maintain references and interact with those libraries safely:


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

function ChartComponent(props: { data: number[] }) {
  const chartRef = useRef<HTMLCanvasElement | null>(null);
  const chartInstance = useRef<Chart | null>(null);

  useEffect(() => {
    if (chartRef.current) {
      chartInstance.current = new Chart(chartRef.current, {
        type: 'line',
        data: {
          labels: [...Array(props.data.length).keys()],
          datasets: [
            {
              label: 'Data',
              data: props.data,
              borderColor: 'blue',
            },
          ],
        },
      });
    }

    return () => {
      if (chartInstance.current) {
        chartInstance.current.destroy();
      }
    };
  }, [props.data]);

  return <canvas ref={chartRef} />;
}
Enter fullscreen mode Exit fullscreen mode

Here, chartRef holds a reference to the canvas element, and chartInstance holds a reference to the Chart instance. We create and destroy the chart safely within the useEffect hook.

Conclusion
The useRef hook in React, when combined with TypeScript, is a powerful tool that allows you to work with mutable values and interact with the DOM efficiently while maintaining type safety. Understanding the various use cases and best practices for useRef will help you write cleaner, more performant React components. Whether you're managing DOM elements, tracking previous values, caching data, or interacting with external libraries, useRef is an invaluable addition to your React toolkit.

Top comments (7)

Collapse
 
buchslava profile image
Vyacheslav Chub

The fifth case is "Refs for cross-component interaction". You can read about 'useImperativeHandle' and 'forwardRef' in one of my articles here dev.to/valorsoftware/zero-cost-way...

Does it make sense?

Collapse
 
lilb0nd profile image
⚡ Anuj Yadav ⚡

Thanks for sharing!!

Collapse
 
dshaw0004 profile image
Dipankar Shaw

Can you explain the number 2
I am having difficulty to understand "Storing Previous Values"

Collapse
 
kirubelkinfe profile image
Kirubel Kinfe • Edited

Sure Check the code below,
App.tsx

import { useState } from "react";
import ValueChangeDetector from "./ValueChangeDetector";

export default function App() {
  const [value, setValue] = useState(1);
  return (
    <>
      <button onClick={() => setValue((prev) => prev + 1)}>Change</button>
      <ValueChangeDetector value={value} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

ValueChangeDetector.tsx

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

function ValueChangeDetector({ value: number }) {
  const prevValueRef = useRef<number | undefined>();

  useEffect(() => {
    if (prevValueRef.current !== undefined && prevValueRef.current !== value) {
      // you can perform any operation with the previous value here.
      console.log("Value changed:", prevValueRef.current, "->", value);
    }
    prevValueRef.current = value;
  }, [value]);

  return <div>{value}</div>;
}
export default ValueChangeDetector;
Enter fullscreen mode Exit fullscreen mode

try to run this code and check your console...

Collapse
 
zessu profile image
A Makenzi

you basically store a reference to an older value essentially caching it. This might be useful if you wanted to get a certain value across renders

Collapse
 
overflow profile image
overFlow

I am the master of welcomes...and I masterfully welcome you ...: to dev.to. Welcome.

Collapse
 
kirubelkinfe profile image
Kirubel Kinfe

Thank You