Memoization Hooks

Back

Loading concept...

React Memoization Hooks: Your Memory-Saving Superpower 🧠

The Kitchen Analogy

Imagine you’re a chef in a busy restaurant. Every time someone orders the same dish, would you:

A) Calculate the recipe from scratch, measure every ingredient again, and follow every step?

B) Remember you already made that exact dish 5 minutes ago and use the same recipe you figured out?

Smart chefs pick B! That’s exactly what memoization does in React. It remembers results so you don’t waste time doing the same work twice.


What is Memoization?

Memoization = Memory + Optimization

It’s like having a super smart notebook where you write down answers. When someone asks the same question, you just look it up instead of solving it again!

// Without memoization (solving every time)
function add(a, b) {
  console.log("Calculating...");
  return a + b;
}
add(2, 3); // "Calculating..." → 5
add(2, 3); // "Calculating..." → 5 (again!)

// With memoization (remembering)
// React checks: "Did I see 2+3 before?
// Yes! Here's 5!"

🎯 useMemo Hook

What is it?

useMemo is like having a smart assistant who remembers the answer to expensive calculations.

When to use it?

When you have a slow calculation that doesn’t need to run every time your component updates.

The Recipe

const result = useMemo(() => {
  // Your slow calculation here
  return expensiveWork(data);
}, [data]); // Only recalculate when data changes

Real Example: Filtering a Big List

function StudentList({ students, search }) {
  // WITHOUT useMemo: Filters on EVERY render
  // const filtered = students.filter(
  //   s => s.name.includes(search)
  // );

  // WITH useMemo: Only filters when needed
  const filtered = useMemo(() => {
    console.log("Filtering students...");
    return students.filter(
      s => s.name.includes(search)
    );
  }, [students, search]);

  return (
    <ul>
      {filtered.map(s => <li>{s.name}</li>)}
    </ul>
  );
}

Visual Flow

graph TD A["Component Renders"] --> B{Did dependencies change?} B -->|Yes| C["Run calculation"] B -->|No| D["Return cached result"] C --> E["Cache new result"] E --> F["Use result"] D --> F

🎯 useCallback Hook

What is it?

useCallback is like giving your function a permanent ID card. React remembers it’s the same function, not a new one.

Why do functions need this?

Every time your component renders, it creates new functions. Even if they do the exact same thing!

function Parent() {
  // This function is NEW every render!
  const handleClick = () => {
    console.log("Clicked!");
  };

  return <Child onClick={handleClick} />;
}

This causes Child to re-render unnecessarily because it thinks it got a “new” function!

The Fix: useCallback

function Parent() {
  // Same function every render
  const handleClick = useCallback(() => {
    console.log("Clicked!");
  }, []); // No dependencies = never changes

  return <Child onClick={handleClick} />;
}

Real Example: Search Handler

function SearchBox({ onSearch }) {
  const [query, setQuery] = useState("");

  // Stable function reference
  const handleSubmit = useCallback(() => {
    onSearch(query);
  }, [query, onSearch]);

  return (
    <div>
      <input
        value={query}
        onChange={e => setQuery(e.target.value)}
      />
      <button onClick={handleSubmit}>
        Search
      </button>
    </div>
  );
}

Quick Comparison

useMemo useCallback
Remembers values Remembers functions
Returns calculation result Returns the function itself
useMemo(() => value, []) useCallback(fn, [])

✅ When to Memoize

The Traffic Light Rule 🚦

GREEN - DO memoize when:

  1. Expensive calculations (loops over 1000+ items)
  2. Passing callbacks to optimized children (React.memo)
  3. Used as dependency in other hooks
  4. Referential equality matters (object comparison)

RED - DON’T memoize when:

  1. Simple calculations (adding two numbers)
  2. Primitive values (strings, numbers, booleans)
  3. Not passed to children
  4. Component renders infrequently

Decision Flowchart

graph TD A["Should I memoize?"] --> B{Is it expensive?} B -->|Yes| C["useMemo"] B -->|No| D{Is it a function for child?} D -->|Yes| E{Is child memoized?} E -->|Yes| F["useCallback"] E -->|No| G["Probably not needed"] D -->|No| H[Don't memoize]

Good Examples

// ✅ GOOD: Expensive calculation
const sortedData = useMemo(() => {
  return [...bigArray].sort((a, b) =>
    a.score - b.score
  );
}, [bigArray]);

// ✅ GOOD: Callback to memoized child
const handleDelete = useCallback((id) => {
  setItems(prev =>
    prev.filter(item => item.id !== id)
  );
}, []);

// Memoized child benefits from stable callback
<MemoizedList onDelete={handleDelete} />

Bad Examples

// ❌ BAD: Simple calculation
const total = useMemo(() => {
  return price + tax;
}, [price, tax]);
// Just write: const total = price + tax;

// ❌ BAD: Not passed anywhere
const format = useCallback((text) => {
  return text.toUpperCase();
}, []);
// Just write: const format = (text) => ...

⚠️ Memoization Pitfalls

Pitfall #1: Forgetting Dependencies

// 🐛 BUG: Missing 'multiplier' in deps
const calculate = useMemo(() => {
  return value * multiplier;
}, [value]); // Should include multiplier!

// ✅ FIX: Include all dependencies
const calculate = useMemo(() => {
  return value * multiplier;
}, [value, multiplier]);

Pitfall #2: Over-Memoization

Memoization isn’t free! It has a cost:

// 🐛 WASTEFUL: Simple string
const greeting = useMemo(() => {
  return `Hello, ${name}!`;
}, [name]);

// ✅ BETTER: Just compute it
const greeting = `Hello, ${name}!`;

Memoization costs:

  • Memory to store cached value
  • Comparison checks every render
  • More code to maintain

Pitfall #3: Memoizing Without Memoized Children

// 🐛 POINTLESS: Child isn't memoized
const Parent = () => {
  const handler = useCallback(() => {
    doSomething();
  }, []);

  // RegularChild re-renders anyway!
  return <RegularChild onClick={handler} />;
};

// ✅ ACTUALLY HELPS: Child is memoized
const MemoChild = React.memo(RegularChild);

const Parent = () => {
  const handler = useCallback(() => {
    doSomething();
  }, []);

  return <MemoChild onClick={handler} />;
};

Pitfall #4: Creating Objects in Dependencies

// 🐛 BUG: New object every render!
const result = useMemo(() => {
  return processData(data);
}, [{ id: 1, name: "test" }]); // Always new!

// ✅ FIX: Use stable reference
const config = useMemo(() => ({
  id: 1,
  name: "test"
}), []);

const result = useMemo(() => {
  return processData(data);
}, [config]);

Pitfall #5: Expensive Dependency Comparisons

// 🐛 PROBLEM: Large object comparison
const result = useMemo(() => {
  return transform(hugeObject);
}, [hugeObject]); // Slow comparison!

// ✅ BETTER: Use specific properties
const result = useMemo(() => {
  return transform(hugeObject);
}, [hugeObject.id, hugeObject.version]);

The Golden Rules Summary

  1. Measure first - Don’t guess, profile!
  2. Start without - Add memoization only when needed
  3. Include all deps - Trust the ESLint rules
  4. Pair with React.memo - useCallback alone isn’t enough
  5. Keep deps stable - Don’t create objects inline

Quick Reference

// useMemo: Cache expensive VALUES
const value = useMemo(() => {
  return heavyCalculation(input);
}, [input]);

// useCallback: Cache FUNCTIONS
const fn = useCallback((arg) => {
  doSomething(arg, dependency);
}, [dependency]);

// React.memo: Prevent re-renders
const MemoComponent = React.memo(Component);

Remember! 🎯

Premature optimization is the root of all evil. — Donald Knuth

Only memoize when you’ve measured a real performance problem. Your app is probably fast enough without it!

Happy coding! 🚀

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.