LogoTRUONG PHAM
Home
Projects
Portfolio
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
  • Portfolio
  • Blogs
  • YouTube
  • Contact

Connect

  • GitHub
  • LinkedIn
  • Email
  • YouTube

© 2024 TRUONG PHAM. © All rights reserved.

Privacy PolicyTerms of Service
Back/50 FRONTEND LESSONS – HARD-EARNED EXPERIENCES
Blog #14: The Domino Effect – When one small change crashes the whole Render system

Blog #14: The Domino Effect – When one small change crashes the whole Render system

To my early React days, when I wondered why changing one text color made the whole 1000-row table redraw from scratch.

April 18, 2024·4 min read

Hey there,

I remember how frustrated you were. You had a complex Dashboard with many child components. You just wanted to add a tiny countdown clock in the corner of the screen. You created a seconds state in the parent component (App), and every second you called setSeconds.

The result? Every second, the entire chart, data table, and user list... everything gave a "slight stutter." Typing in the search bar suddenly felt laggy. You panicked: "Why is React so slow?". You had no idea you'd accidentally triggered an unnecessary "general mobilization" re-render on an application-wide scale.

1. Simple Explanation: The Story of a Family

Imagine a 3-generation family living together. You are the father (Root Component). The supervisor is the grandfather. Your children are the child UIs. Every second, you stand in the middle of the house and shout: "It's now the 32nd second!".

As soon as you shout, everyone in the house—no matter what they're busy doing—must stop, look at you, self-check if the "32nd second" information relates to them, and then continue working. If you shout every second, everyone will go crazy and no one will get anything done properly.

2. Current Perspective: Why 'State Placement' matters?

Before, you thought "if there's data, just push it up to the parent for easy management." But now I understand: The closer the state is to where it's used, the smoother the app. React is incredibly fast, but comparing the Virtual DOM for thousands of child components every second is a massive waste of resources. When you change state in a Parent Component, by default, all its Child Components must re-render (unless you use memo, but that's a different story about costs we discussed in article #13).

3. Concrete Example: The "Global State" Trap

You used to do this because it was "convenient":

// Junior's way: "Top floor" holds everything
const Dashboard = () => {
  const [timer, setTimer] = useState(0); // State changes constantly
  const [data, setData] = useState([]); // Heavy data

  useEffect(() => {
    const id = setInterval(() => setTimer(t => t + 1), 1000);
    return () => clearInterval(id);
  }, []);

  return (
    <div>
      <TimerDisplay time={timer} />
      {/* 
        Every time timer changes, BigExpensiveChart will re-render 
        even though data hasn't changed! 
      */}
      <BigExpensiveChart data={data} />
    </div>
  );
};

Why is this a problem? Because BigExpensiveChart must redo its chart calculation logic once every second. If the user's machine is weak, they'll see the browser freeze continuously.

Sustainable Approach (State Colocation):

// Push state as low as possible
const Timer = () => {
  const [timer, setTimer] = useState(0);
  useEffect(() => {
    const id = setInterval(() => setTimer(t => t + 1), 1000);
    return () => clearInterval(id);
  }, []);
  return <TimerDisplay time={timer} />;
};

const Dashboard = () => {
  const [data, setData] = useState([]);
  
  return (
    <div>
      <Timer /> {/* Only Timer re-renders every second, Dashboard and Chart stay still! */}
      <BigExpensiveChart data={data} />
    </div>
  );
};

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

CriteriaDoing it Fast (Push it all to Parent)Doing it Sustainably (State Colocation)
Data ManagementVery easy, everything is available at parentTakes effort to think about where state should be
Render PerformanceDisaster as the app growsAbsolutely optimized, isolated renders
DebuggingHard to find what causes re-rendersEasy to isolate errors in small components
MindsetMonolithic "monster" styleAtomic style

Juniors often choose the left column because "it's tedious to pass props." But a Senior will choose the right column because they respect every CPU cycle of the user's computer.

5. Practical Lesson: "Meaningless" Render

I once encountered a bug where when a user typed into an Input in the Header, a list of 500 articles in the Body would lag. It turned out that Input was changing a state at a Context Provider wrapped around the entire application. The whole app was "shaking" every time the user typed a character.

The biggest lesson is: Keep state in the smallest cage possible. If a state only serves one component, never let it escape. "Lift state up" only when you really need to share data between siblings; don't do it out of laziness.

Remember: A smooth application isn't because it uses cool libraries, but because its hundreds of child components know when to "be quiet" and stay still.


Notes for my 25-year-old self, back when I learned to 'restrain' re-renders.

Previous: Blog #13: When optimization becomes a burden – Don't 'memo' everything you seeAll posts in this seriesNext: Blog #15: The 10,000-Row Lesson – When Table Virtualization saved my career