Mastering 'useRef' in React with TypeScript: 4 Different Use-Cases for 'useRef'
Tags: react, typescript, javascript, reacthooks
Published: 2023-09-02 16:03:14

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>;
}
```
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} />;
}
```
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>;
}
```
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>
  );
}
```
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} />;
}
```

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.