Page cover
Performance isn't about premature optimization -
it's about knowing when to memoize,
understanding the cost &
measuring real impact.

React Hooks: Deep Dive into useCallback and useMemo

React Hooks: Deep Dive into useCallback and useMemo
React December 10, 2024 8 min read

Master performance optimization in React by understanding when and how to use useCallback and useMemo hooks effectively.

Prerequisites

Essential knowledge and skills you should have before starting

Solid understanding of React fundamentals and component lifecycle
Basic knowledge of React Hooks (useState, useEffect)
Understanding of JavaScript closures and references

React's useCallback and useMemo hooks are powerful tools for performance optimization, but they're often misused. Let's explore when you actually need them and how to use them effectively.

Understanding useCallback

useCallback returns a memoized version of a callback function. It's useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

typescript
import { useCallback, useState } from 'react';

function ParentComponent() {
  const [count, setCount] = useState(0);
  const [todos, setTodos] = useState([]);

  // Without useCallback, this function is recreated on every render
  const addTodo = useCallback((text: string) => {
    setTodos(prev => [...prev, { id: Date.now(), text }]);
  }, []); // Empty dependency array means it never changes

  return (
    <>
      <button onClick={() => setCount(count + 1)}>
        Count: {count}
      </button>
      <TodoList onAddTodo={addTodo} />
    </>
  );
}

Understanding useMemo

useMemo memoizes the result of a computation. Use it for expensive calculations that don't need to run on every render.

typescript
import { useMemo } from 'react';

function DataTable({ data, filter }: Props) {
  // Expensive computation - only recalculates when data or filter changes
  const filteredData = useMemo(() => {
    return data.filter(item =>
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [data, filter]);

  return (
    <table>
      {filteredData.map(item => (
        <tr key={item.id}>
          <td>{item.name}</td>
        </tr>
      ))}
    </table>
  );
}

When NOT to Use Them

Don't use these hooks prematurely. Most of the time, React is fast enough without memoization. Only optimize when you have measurable performance issues. Overuse adds complexity and can actually hurt performance due to the overhead of comparison checks.

The Golden Rules

Use useCallback when passing callbacks to memoized child components. Use useMemo for expensive calculations with stable dependencies. Always measure before optimizing. And remember: premature optimization is the root of all evil.

Tags: React Hooks Performance Optimization

Found this helpful?

I write about software engineering, architecture, and best practices. Check out more articles or get in touch.