blogsaboutContactPrivacy PolicyTerms & Conditions
Reactreact useref hook

How to Use useRef in React: DOM Access, Typescript & More

February 4, 2025

10 min read

124 views

React provides powerful built-in hooks like useState, useReducer, and useEffect to manage state and side effects. But what about useRef? In this article, we will explore how to use the React useRef hook, complete with practical examples. We’ll also break down the key differences between useRef and useState, and show you how to integrate React useRef with TypeScript to avoid common type errors.

What is React useRef Hook?

The React useRef hook is a built-in hook that allows you to persist values across renders without causing re-renders. It is commonly used to reference DOM elements and store mutable values.

Basic Usage

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

.jsx
1import React, { useRef } from 'react'; // import useRef
2
3function MyComponent() {
4const myRef = useRef(null); // Creating a ref
5
6// Accessing the current value of the ref
7console.log(myRef.current);
8
9return <div ref={myRef}>Basic Example of useRef</div>;
10}
11

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.

Usage of useRef hook with TypeScript

The useRef hook is simple to use but becomes even more powerful when paired with TypeScript. Here’s how you can get started:

  1. Import useRef

    First, import useRef from React:

    .tsx
    1import { useRef } from 'react';
  2. Create a Ref

    Next, create a ref using useRef. With TypeScript, you’ll need to specify the type of the ref (e.g., HTMLInputElement for an input field):

    .tsx
    1const inputRef = useRef<HTMLInputElement>(null);  
    2// Refs are initially set to null because the DOM element doesn’t exist yet.
  3. Attach the Ref to a DOM Element

    Pass the ref to the ref attribute of a DOM element:

    .tsx
    1<input ref={inputRef} type="text" />  
  4. Access the Ref

    Use ref.current to access the DOM element or stored value:

    .tsx
    1const focusInput = () => {  
    2    inputRef.current?.focus(); // Focus the input field 
    3 };

Example: Focus an Input Field

Here’s a complete example of using useRef hook with TypeScript to focus an input field:

.tsx
1import { useRef } from 'react';  
2
3function App() {  
4const inputRef = useRef<HTMLInputElement>(null);  
5
6const focusInput = () => {  
7  inputRef.current?.focus();  
8};  
9
10return (  
11  <div>  
12    <input ref={inputRef} type="text" placeholder="Type something..." />  
13    <button onClick={focusInput}>Focus Input</button>  
14  </div>  
15);  
16}  
17
18export default App;  

Key Points to Remember

  • TypeScript Generics: Always specify the type of your ref (e.g., HTMLInputElement, number, etc.).
  • Initial Value: Initialize refs with null for DOM elements.
  • Optional Chaining: Use ? to safely access ref.current.

Advanced useRef Examples

Expand beyond the basics with real-world use cases:

  1. Tracking Previous State:

    Sometimes, you need to know the previous value of a state or prop in React. Since state updates are asynchronous, you can’t directly compare the current and previous values. This is where react useRef hook comes in hand.

    How It Works

    • useRef persists values between renders without triggering re-renders.
    • You can store the previous value in a ref and update it after the state changes.

    Example: Tracking Previous State

    .tsx
    1import { useState, useRef, useEffect } from 'react';  
    2
    3 function App() {  
    4   const [count, setCount] = useState<number>(0);  
    5   const prevCountRef = useRef<number>(0);  
    6
    7   useEffect(() => {  
    8       prevCountRef.current = count; // Update the ref with the current value  
    9   }, [count]); // Run this effect whenever count changes  
    10
    11   return (  
    12       <div>  
    13       <p>Current Count: {count}</p>  
    14       <p>Previous Count: {prevCountRef.current}</p>  
    15       <button onClick={() => setCount(count + 1)}>Increment</button>  
    16       </div>  
    17   );  
    18   }  
    19
    20   export default App;  

    Explanation

    • State: count holds the current value.
    • Ref: prevCountRef stores the previous value.
    • Effect: After count changes, the effect updates prevCountRef.current with the new value.

Common Mistakes and Fixes with useRef

While useRef is a powerful tool, it’s easy to make mistakes if you’re not careful. Here are some common pitfalls of using useRef hook with TypeScript and how to fix them:

  1. ref.current is null

    Mistake: Trying to access ref.current before the DOM element is ready.
    .tsx
    1const inputRef = useRef<HTMLInputElement>(null);  
    2inputRef.current.focus(); // Error: current is null  
    Fix: Use optional chaining (?.) to safely access ref.current:
    .tsx
    1inputRef.current?.focus(); // No error if current is null   
    Why It Happens: Refs are initialized with null and only get assigned when the DOM element is rendered.
  2. Overusing useRef for State Management

    Mistake: Using useRef to store values that should trigger re-renders.
    .tsx
    1const countRef = useRef<number>(0);  
    2countRef.current += 1; // No re-render, UI won't update   
    Fix: Use useState for values that affect the UI:
    .tsx
    1const [count, setCount] = useState<number>(0);  
    2setCount(count + 1); // Triggers re-render   
    Why It Happens: useRef doesn’t trigger re-renders, so it’s not suitable for state that should update the UI.
  3. Forgetting to Clean Up Refs

    Mistake: Not cleaning up refs (e.g., intervals, timeouts) when the component unmounts.
    .tsx
    1const timerRef = useRef<number | null>(null);  
    2
    3useEffect(() => {  
    4timerRef.current = setInterval(() => {  
    5console.log('Timer running');  
    6}, 1000);  
    7}, []);  
    8
    9// No cleanup: Memory leak!  
    Fix: Always clean up refs in the useEffect cleanup function:
    .tsx
    1useEffect(() => {  
    2const timer = setInterval(() => {  
    3   console.log('Timer running');  
    4}, 1000);  
    5
    6timerRef.current = timer;  
    7return () => clearInterval(timer); // Cleanup  
    8}, []);  
    Why It Happens: Failing to clean up can lead to memory leaks and unexpected behavior.

useRef vs useState: Use Cases

useRef:

  • Access DOM elements (e.g., focus an input field or control a video player).
  • Persist mutable values without triggering re-renders (e.g., previous state values).
  • Integrate with third-party libraries (e.g., animations or charting libraries).
  • Store a timer ID (e.g., setInterval or setTimeout).

useState:

  • Track form input values (e.g., text fields, checkboxes, or dropdowns).
  • Toggle UI state (e.g., show/hide a modal or toggle a button’s active state).
  • Manage dynamic data that requires re-renders (e.g., counters, to-do lists).
  • Control component behavior based on state changes (e.g., fetching data when a button is clicked).

Check also SEO strategies to Boost website ranking.

Frequently Asked Questions

  • Q: What is useRef in React?

    A: useRef is a React hook that lets you persist values between renders without triggering re-renders. It’s commonly used to access DOM elements, store mutable values, and integrate with third-party libraries.

  • Q: How is useRef different from useState?

    A: useRef persists values without triggering re-renders, while useState manages state that triggers re-renders when updated. Use useRef for DOM access and useState for UI state management.

  • Q: Why is my useRef value not updating?

    A: useRef doesn’t trigger re-renders, so changes to ref.current won’t update the UI. If you need re-renders, use useState instead.

  • Q: Why is useRef current null?

    A: Refs are initialized with null and only get assigned when the DOM element is rendered. Use optional chaining (?.) to safely access ref.current.

If you found this blog helpful, feel free to share it with others who may benefit from it!

Share this article

On this page: