React.memo, useMemo & useCallback

0/2 in this phase0/36 across the roadmap

📖 Concept

By default, when a parent component re-renders, all of its children also re-render. In a small app, this is fine. In a large app, it can lead to lag.

1. React.memo: A Higher-Order Component that skips re-rendering a component if its props haven't changed. It uses "shallow comparison".

2. useMemo: Memoizes the result of a calculation. Use it when you have an expensive operation (like filtering a large list) that you only want to re-run when specific inputs change.

3. useCallback: Memoizes a function instance. In JavaScript, functions are objects, and every time a component re-renders, it creates a "new" version of its functions. Passing these new functions to a memoized child will break React.memo. useCallback prevents this by returning the same function instance across renders.

The Golden Rule: Don't optimize prematurely! Memoization adds its own overhead. Use it only when you have a measurable performance problem.

💻 Code Example

codeTap to expand ⛶
1import React, { useState, useMemo, useCallback } from 'react';
2
3// 1. Memoized Child Component
4const HeavyChild = React.memo(({ onAction, items }) => {
5 console.log('HeavyChild re-rendering');
6 return (
7 <div className="p-4 border mt-4">
8 <p>I only re-render if my props change!</p>
9 <button onClick={onAction} className="bg-blue-500 text-white p-2">Click Me</button>
10 <ul>{items.map(i => <li key={i}>{i}</li>)}</ul>
11 </div>
12 );
13});
14
15function Parent() {
16 const [count, setCount] = useState(0);
17 const [items] = useState(['Apple', 'Banana', 'Cherry']);
18
19 // 2. useCallback: Prevents creating a new function on every render
20 const handleAction = useCallback(() => {
21 alert('Action triggered from child!');
22 }, []); // Empty deps = always the same function
23
24 // 3. useMemo: Only re-sort the list if 'items' changes
25 const sortedItems = useMemo(() => {
26 console.log('Sorting items...');
27 return [...items].sort();
28 }, [items]);
29
30 return (
31 <div className="p-10">
32 <h2 className="text-2xl font-bold">Performance Demo</h2>
33 <p>Parent Count: {count}</p>
34 <button
35 onClick={() => setCount(c => c + 1)}
36 className="bg-gray-200 p-2"
37 >
38 Increment Parent (Child won't re-render)
39 </button>
40
41 <HeavyChild onAction={handleAction} items={sortedItems} />
42 </div>
43 );
44}

🏋️ Practice Exercise

  1. Build a list of 500 items and a search filter. Use useMemo to optimize the filtering.
  2. Create a child component and observe it re-rendering using console.log. Wrap it in React.memo and see what stops it.
  3. Pass a function from a parent to a memoized child. Notice the child re-renders even with React.memo. Fix it using useCallback.
  4. Use the 'Why Did You Render' (wdyr) library to identify components that are re-rendering unnecessarily.

⚠️ Common Mistakes

  • Wrapping every single function and calculation in useMemo/useCallback (this is often slower due to overhead).

  • Forgetting that React.memo only does a shallow comparison (it won't detect changes inside objects or arrays).

  • Not including all required dependencies in the dependency array.

  • Using memoization for simple values that are cheap to calculate.

💼 Interview Questions

🎤 Mock Interview

Practice a live interview for React.memo, useMemo & useCallback