LogoTRUONG PHAM
Home
Projects
Blogs
YouTube
Contact

Newsletter

Stay updated with technical artifacts and engineering insights.

LogoTRUONG PHAM

Building scalable software and sharing insights on technology & life.

Sitemap

  • Home
  • Projects
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back
Blog #13: When optimization becomes a burden – Don't 'memo' everything you see
50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

Blog #13: When optimization becomes a burden – Don't 'memo' everything you see

To my past self when I first discovered useMemo and useCallback, thinking that wrapping everything in 'memo' would turn me into a performance master.

TP
Truong PhamSoftware Engineer
PublishedApril 15, 2024

Hey there,

I remember the day you discovered useMemo, useCallback, and React.memo. It felt like you'd just been handed a magic wand that could solve all performance problems. You started scattering useMemo everywhere, from simple addition and subtraction to arrays with only a few elements. You confidently told the team: "Don't worry, I've memoized everything, the app will be smooth as silk!".

But did you know that very "dedication" was quietly stifling our application?

1. Simple Explanation: The Notebook Story

Imagine you're a student learning math. The supervisor asks you to calculate 5 + 10. You take out an expensive notebook and carefully write: "If asked for 5 + 10, the result is 15". Then you put the notebook into a safe. Next time the supervisor asks, you have to go to the effort of opening the safe, flipping to the right page to read the result—15.

Doing the mental math of 5 + 10 takes 0.1 seconds, but writing it down and looking it up takes 2 seconds. That is the mistake called Over-memoization.

2. Current Perspective: The hidden cost of optimization

Your old self only saw the benefit of "not having to recalculate." But my current self sees the costs you ignored:

  • Memory: Every time you use useMemo, React must store that value and the dependency array in memory. The more memos, the more RAM used.
  • Comparison Cost: On every render, React must run a loop to compare each element in the dependency array to see if anything has changed (===). If your original calculation is faster than running this comparison loop, you're slowing the app down.
  • Code Complexity: Your code is now cluttered with square brackets and curly braces, making it extremely difficult for others to read and maintain later on.

3. Concrete Example: The "Memo Everything" Trap

You used to write like this and take great pride in it:

// Junior's mistake: Memoizing even breath
const MyComponent = ({ name, age }) => {
  // Extremely simple string concatenation still memoized!
  const greeting = useMemo(() => `Hello, ${name}`, [name]);

  // A simple function not passed anywhere but still useCallback!
  const handleClick = useCallback(() => {
    console.log('Clicked');
  }, []);

  return <div onClick={handleClick}>{greeting} - {age}</div>;
};

Why is this a problem? Because useMemo and useCallback in this case are completely meaningless. handleClick isn't passed down to any React.memo component below, so trying to keep its reference brings no benefit to re-renders. You're just wasting memory and CPU for useless comparison tasks.

Sustainable Approach (Pragmatic Approach):

// Only memo when truly necessary
const MyComponent = ({ items, onSelect }) => {
  // Only memo for complex calculations (e.g., filter/sort thousands of records)
  const sortedItems = useMemo(() => {
    return complexSort(items);
  }, [items]);

  // Only useCallback when passing down to a component wrapped in React.memo
  const handleSelect = useCallback((id) => {
    onSelect(id);
  }, [onSelect]);

  return (
    <div>
      {sortedItems.map(item => (
        <HugeExpensiveListItem key={item.id} data={item} onSelect={handleSelect} />
      ))}
    </div>
  );
};

4. Comparison: "Doing it Fast" vs "Doing it Sustainably"

CriteriaDoing it Fast (Wrap all in Memo)Doing it Sustainably (Profile before Memo)
MindsetMemo to be safe, just in case it's fasterOnly optimize when a bottleneck is detected
Actual PerformanceCan be slower due to memo management overheadTruly optimized where needed
Code CleanlinessClunky, lots of boilerplateClear, easy to understand
Technical DebtHard to refactor due to tangled dependenciesEasy to change and expand

Juniors often choose to "wrap everything to be safe." But a true Senior will say: "Don't optimize prematurely."

5. Practical Lesson: Don't guess, measure

I once spent a whole day finding out why an extremely simple Component rendered slowly, even though it was wrapped in memo. Finally discovered the error: I passed an empty object {} as a dependency to useMemo, causing it to always be recalculated, plus the cost of the useMemo itself, making the experience much worse.

The biggest lesson is: Use the Profiler. Chrome browser has a great tool called "React Profiler." Turn it on, see which components are actually re-rendering a lot and taking time, then decide whether to use memo or not.

Don't use feelings to optimize. Use data. And most importantly, remember: simple code is the most powerful code.


Notes for my 24-year-old self, back when I lost the illusion of 'magic wand' performance.

Series • Part 13 of 50

50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES

NextBlog #14: The Domino Effect – When one small change crashes the whole Render system
Blog #12: Don't bring the whole supermarket home just to buy a loaf of bread
08Blog #8: Memory Trash – The price of forgetting to 'clean up' your Event Listeners09Blog #9: Safari – The new 'Internet Explorer' of the modern era?10Blog #10: Specificity War – When !important marks the start of a civil war11Blog #11: Don't wait until 'heavy web' to lose weight for your Bundle12Blog #12: Don't bring the whole supermarket home just to buy a loaf of bread13Blog #13: When optimization becomes a burden – Don't 'memo' everything you seeReading14Blog #14: The Domino Effect – When one small change crashes the whole Render system15Blog #15: The 10,000-Row Lesson – When Table Virtualization saved my career16Blog #16: Misplaced Debounce – When a localized solution becomes a system burden17Blog #17: Images vs JS – The battle for priority in the Critical Path18Blog #18: Laggy Animations – When the touch is no longer smooth
TP

Written by Truong Pham

Software Engineer passionate about building high-performance systems and meaningful experiences.

Read more articles