React
Memoization

Memoization in React

Memoization is an optimization technique used in React to improve performance by caching the results of expensive computations. This technique can significantly reduce unnecessary re-renders and enhance the overall efficiency of your application.

What is Memoization?

Memoization is the process of storing the results of expensive function calls and returning the cached result when the same inputs occur again. In React, this concept is applied to components and their props to prevent unnecessary re-renders.

Why Use Memoization?

React components re-render when their state or props change. However, sometimes these re-renders are unnecessary, especially for complex components or those receiving the same props. Memoization helps by:

  1. Reducing unnecessary re-renders
  2. Improving performance for computationally expensive operations
  3. Optimizing the rendering of child components

React Hooks for Memoization

React provides two main hooks for memoization:

  1. useMemo: For memoizing values
  2. useCallback: For memoizing functions

useMemo

useMemo is used to memoize the result of a computation. It only recomputes the memoized value when one of the dependencies has changed.

Here's an example of how to use useMemo:

import React, { useMemo, useState } from 'react';
 
interface Props {
  numbers: number[];
}
 
function ExpensiveCalculation({ numbers }: Props) {
  const [count, setCount] = useState(0);
 
  const expensiveSum = useMemo(() => {
    console.log('Calculating sum...');
    return numbers.reduce((acc, num) => acc + num, 0);
  }, [numbers]);
 
  return (
    <div>
      <p>Sum: {expensiveSum}</p>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment Count</button>
    </div>
  );
}
 
function App() {
  const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
 
  return <ExpensiveCalculation numbers={numbers} />;
}
 
export default App;

Use Cases

  • Large Data Sets
  • Complex Calculations
  • Deeply Nested Components
  • Expensive Re-renders
import React, { useMemo, useState, useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
 
interface User {
  id: number;
  name: string;
  email: string;
}
 
function UserDashboard() {
  const [filter, setFilter] = useState('');
 
  // Fetch users data
  const { data: users = [] } = useQuery<User[]>({
    queryKey: ['users'],
    queryFn: async () => {
      const response = await fetch('https://api.example.com/users');
      if (!response.ok) throw new Error('Failed to fetch users');
      return response.json();
    },
  });
 
  // Memoize the filtered users
  const filteredUsers = useMemo(() => {
    console.log('Filtering users...');
    return users.filter(user => 
      user.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [users, filter]);
 
  return (
    <div>
      <input
        type="text"
        value={filter}
        onChange={(e) => setFilter(e.target.value)}
        placeholder="Filter users..."
      />
      <ul>
        {filteredUsers.map(user => (
          <li key={user.id}>{user.name} ({user.email})</li>
        ))}
      </ul>
    </div>
  );
}

In this example: We use TanStack Query to fetch user data from an API. The filteredUsers value is memoized using useMemo. It depends on both the users data and the filter state. The expensive filtering operation only runs when either the users data or the filter changes. If the component re-renders for any other reason, the memoized filteredUsers value is reused without recalculation. This approach is beneficial because: The API data (users) likely doesn't change often, so we don't need to re-filter on every render. When the user types in the filter input, we do need to recalculate, and useMemo allows for that. If there are other state changes in the component that cause re-renders, we avoid unnecessary filtering operations. Remember, while this is a good optimization, it's important to profile your app to ensure that memoization is actually providing a performance benefit, as it does come with its own small overhead.