When developing React applications, managing references to DOM elements and other mutable objects efficiently can be crucial for creating performant and interactive user interfaces. The useRef hook is a versatile tool that provides an elegant solution for these scenarios. In this blog post, we’ll dive deep into the useRef hook, exploring its usage, benefits, and practical examples.

What is useRef?

useRef is a React hook that returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). This ref object persists throughout the lifetime of the component, enabling you to store and maintain references to DOM elements or any other mutable value.

Basic Usage

To get started, let’s look at a basic example of how to use the useRef hook to reference a DOM element:

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

function FocusInput() {
  const inputRef = useRef(null);

  useEffect(() => {
    // Focus the input element on component mount
    inputRef.current.focus();
  }, []);

  return <input ref={inputRef} type="text" />;
}

export default FocusInput;

In this example, we create a ref using useRef(null) and attach it to an <input> element via the ref attribute. When the component mounts, the useEffect hook sets the focus on the input element using inputRef.current.focus().

Storing Mutable Values

Apart from referencing DOM elements, useRef can also be used to store mutable values that persist across renders without causing re-renders. This is particularly useful for storing values like timers, previous states, or any other mutable object.

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

function Timer() {
  const [count, setCount] = useState(0);
  const timerId = useRef(null);

  const startTimer = () => {
    if (timerId.current === null) {
      timerId.current = setInterval(() => {
        setCount(prevCount => prevCount + 1);
      }, 1000);
    }
  };

  const stopTimer = () => {
    if (timerId.current !== null) {
      clearInterval(timerId.current);
      timerId.current = null;
    }
  };

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

export default Timer;

In this example, we use useRef to store the timerId. This allows us to manage the timer across renders without triggering re-renders when the timerId changes.

Avoiding Unnecessary Re-renders

Using useRef can help avoid unnecessary re-renders, which can be crucial for performance optimization. For instance, if you need to keep a value that should not cause re-renders when updated, useRef is the perfect tool.

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

function ClickCounter() {
  const clickCount = useRef(0);
  const [displayCount, setDisplayCount] = useState(0);

  const handleClick = () => {
    clickCount.current += 1;
    setDisplayCount(clickCount.current);
  };

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <p>Button clicked {displayCount} times</p>
    </div>
  );
}

export default ClickCounter;

Here, clickCount is incremented on each button click, but it doesn’t cause a re-render. Instead, the state displayCount is updated to reflect the current count, ensuring the component re-renders only when necessary.

Accessing Previous Props or State

useRef can also be used to access previous props or state values, which is particularly useful in scenarios where you need to compare current and previous values.

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

function PreviousValue() {
  const [count, setCount] = useState(0);
  const prevCount = useRef();

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

  return (
    <div>
      <p>Current count: {count}</p>
      <p>Previous count: {prevCount.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default PreviousValue;

In this example, prevCount is updated with the current count value in the useEffect hook, allowing you to access the previous count value during the next render.

Conclusion

The useRef hook is a powerful tool in the React ecosystem, offering a flexible and efficient way to reference DOM elements, store mutable values, and access previous state or props. By understanding and utilizing useRef, you can enhance your React applications’ performance and maintainability.

Whether you’re building simple interactive components or complex applications, useRef can be a valuable addition to your React toolkit. Experiment with it in your projects and see how it can simplify your code and improve your development experience.


useRef Interview Questions

Understanding useRef and its applications in React is crucial for managing references to DOM elements and mutable values efficiently. Here are some interview questions that can help assess a candidate’s knowledge and expertise with useRef:

Basic Questions

  1. What is useRef and how is it used in React?
  • Answer: useRef is a React hook that returns a mutable ref object whose .current property is initialized to the passed argument (initialValue). It can be used to reference DOM elements or store any mutable value that persists across renders.
  1. How does useRef differ from useState?
  • Answer: useRef returns a mutable object that does not trigger re-renders when updated, while useState returns a state value and a function to update it, which causes the component to re-render.
  1. Can you provide a basic example of using useRef to focus an input element on component mount?
  • Answer:
import React, { useRef, useEffect } from 'react';

function FocusInput() {
  const inputRef = useRef(null);

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

  return <input ref={inputRef} type="text" />;
}

export default FocusInput;

Intermediate Questions

  1. How would you use useRef to store a mutable value that doesn’t cause a component re-render when updated?
  • Answer:
import React, { useRef, useState } from 'react';

function Counter() {
  const countRef = useRef(0);
  const [displayCount, setDisplayCount] = useState(0);

  const increment = () => {
    countRef.current += 1;
    setDisplayCount(countRef.current);
  };

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

export default Counter;
  1. Explain how useRef can be used to access previous props or state values in a component.
  • Answer:
import React, { useEffect, useRef, useState } from 'react';

function PreviousValue() {
  const [count, setCount] = useState(0);
  const prevCount = useRef();

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

  return (
    <div>
      <p>Current count: {count}</p>
      <p>Previous count: {prevCount.current}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default PreviousValue;

Advanced Questions

  1. Describe a scenario where using useRef is more appropriate than useState or useEffect.
  • Answer: useRef is more appropriate when you need to persist a value across renders without causing a re-render. For example, when storing a timer ID for clearing intervals, managing focus state for input elements, or keeping track of the previous state or prop values for comparison.
  1. Can useRef be used to trigger re-renders in React? Explain your answer.
  • Answer: No, useRef does not trigger re-renders when its value is updated. It is intended for referencing DOM elements or storing mutable values that do not need to trigger component updates. If you need to trigger a re-render, useState or useReducer should be used.
  1. How can useRef be utilized in combination with custom hooks to create reusable logic?
  • Answer: useRef can be used in custom hooks to manage mutable values or DOM references. For example, a custom hook that tracks the previous value of a prop or state can utilize useRef.
import { useRef, useEffect } from 'react';

function usePrevious(value) {
  const ref = useRef();
  useEffect(() => {
    ref.current = value;
  }, [value]);
  return ref.current;
}

// Usage
import React, { useState } from 'react';
import usePrevious from './usePrevious';

function ExampleComponent() {
  const [count, setCount] = useState(0);
  const prevCount = usePrevious(count);

  return (
    <div>
      <p>Current count: {count}</p>
      <p>Previous count: {prevCount}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

export default ExampleComponent;

Practical Questions

  1. How would you implement a custom hook that uses useRef to keep track of how many times a component has rendered?
  • Answer:
import { useRef, useEffect } from 'react';

function useRenderCount() {
  const renderCount = useRef(0);
  useEffect(() => {
    renderCount.current += 1;
  });
  return renderCount.current;
}

// Usage
import React from 'react';
import useRenderCount from './useRenderCount';

function RenderCounter() {
  const count = useRenderCount();

  return <div>Component has rendered {count} times</div>;
}

export default RenderCounter;
  1. Write a component that uses useRef to handle a file input element and programmatically trigger a click on it.
    • Answer:
import React, { useRef } from 'react';

function FileUploader() {
  const fileInputRef = useRef(null);

  const handleButtonClick = () => {
    fileInputRef.current.click();
  };

  return (
    <div>
      <input
        type="file"
        ref={fileInputRef}
        style={{ display: 'none' }}
        onChange={(e) => console.log(e.target.files[0])}
      />
      <button onClick={handleButtonClick}>Upload File</button>
    </div>
  );
}

export default FileUploader;

These questions and answers should help interviewers gauge a candidate’s understanding of useRef and its practical applications in React development.


Read other awesome articles in Medium.com or in akcoding’s posts, you can also join our YouTube channel AK Coding

Share with