Skip to content

Quiz: Re-render Prediction

advanced13 min read

Test Your Re-render Intuition

You've made it through re-render triggers, memo, composition, context, and transitions. Now let's see if you can actually predict what React does under pressure. No guessing here -- you need to trace through the logic step by step.


Scenario 1: The Prop Myth

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>+</button>
      <Child name="Alice" />
    </div>
  );
}

function Child({ name }) {
  console.log('Child rendered');
  return <p>{name}</p>;
}
Quiz
When the button is clicked, does Child re-render? Why?

Scenario 2: memo With Inline Object

const MemoChild = memo(function Child({ style, label }) {
  console.log('Child rendered');
  return <p style={style}>{label}</p>;
});

function Parent() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      <MemoChild style={{ color: 'blue' }} label="Hello" />
    </div>
  );
}
Quiz
Does MemoChild re-render when the button is clicked?

Scenario 3: Children as Props

function Wrapper({ children }) {
  const [expanded, setExpanded] = useState(false);
  console.log('Wrapper rendered');

  return (
    <div>
      <button onClick={() => setExpanded(e => !e)}>Toggle</button>
      {expanded && <div className="details">Details panel</div>}
      {children}
    </div>
  );
}

function App() {
  return (
    <Wrapper>
      <ExpensiveComponent />
    </Wrapper>
  );
}

function ExpensiveComponent() {
  console.log('ExpensiveComponent rendered');
  return <div>Heavy content</div>;
}
Quiz
When the Toggle button is clicked, does ExpensiveComponent re-render?

Scenario 4: Context Cascade

const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('light');

  return (
    <ThemeContext.Provider value={theme}>
      <Header />
      <Content />
      <ThemeToggle onToggle={() => setTheme(t => t === 'light' ? 'dark' : 'light')} />
    </ThemeContext.Provider>
  );
}

const Header = memo(function Header() {
  console.log('Header rendered');
  return <h1>My App</h1>;
});

function Content() {
  const theme = useContext(ThemeContext);
  console.log('Content rendered');
  return <div className={theme}>Content</div>;
}

const ThemeToggle = memo(function ThemeToggle({ onToggle }) {
  console.log('ThemeToggle rendered');
  return <button onClick={onToggle}>Toggle Theme</button>;
});
Quiz
When the theme toggles, which components re-render?

Scenario 5: Same Value setState

function Counter() {
  const [count, setCount] = useState(0);

  console.log('Counter rendered');

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(0)}>Set to 0</button>
    </div>
  );
}
Quiz
If count is already 0, does clicking 'Set to 0' cause a re-render?

Scenario 6: useTransition and Interruption

function App() {
  const [tab, setTab] = useState('home');
  const [isPending, startTransition] = useTransition();

  console.log('App rendered, tab:', tab, 'pending:', isPending);

  return (
    <div>
      <button onClick={() => startTransition(() => setTab('about'))}>
        Go to About
      </button>
      <button onClick={() => setTab('home')}>
        Go Home (urgent)
      </button>
      <TabContent tab={tab} />
    </div>
  );
}

function TabContent({ tab }) {
  // Simulates expensive render (200ms)
  console.log('TabContent rendered:', tab);
  return <div>{tab} content</div>;
}
Quiz
User clicks 'Go to About' (transition), then immediately clicks 'Go Home' (urgent). How many times does TabContent render?

Scenario 7: useMemo and Render Scope

function ProductList({ products, category }) {
  const filtered = useMemo(
    () => products.filter(p => p.category === category),
    [products, category]
  );

  console.log('ProductList rendered');

  return filtered.map(p => <ProductCard key={p.id} product={p} />);
}

function ProductCard({ product }) {
  console.log('ProductCard rendered:', product.name);
  return <div>{product.name}</div>;
}
Quiz
If ProductList re-renders but products and category haven't changed, do the ProductCards re-render?

Scenario 8: The Full Picture

const UserContext = createContext(null);

const Header = memo(function Header() {
  const user = useContext(UserContext);
  console.log('Header rendered');
  return <h1>Welcome, {user.name}</h1>;
});

const Sidebar = memo(function Sidebar() {
  console.log('Sidebar rendered');
  return <nav>Sidebar</nav>;
});

function MainContent({ children }) {
  const [count, setCount] = useState(0);
  console.log('MainContent rendered');

  return (
    <div>
      <button onClick={() => setCount(c => c + 1)}>Count: {count}</button>
      {children}
    </div>
  );
}

function App() {
  const [user, setUser] = useState({ name: 'Alice' });

  return (
    <UserContext.Provider value={user}>
      <Header />
      <MainContent>
        <Sidebar />
      </MainContent>
    </UserContext.Provider>
  );
}
Quiz
When MainContent's count button is clicked, which components re-render?

Key Takeaways

Key Rules
  1. 1Parent re-rendering causes ALL children to re-render by default. This is the most common source of wasted renders.
  2. 2React.memo adds a prop comparison gate. But one unstable prop (inline object or function) defeats the entire memo.
  3. 3Children-as-props is a powerful optimization: elements created by a non-re-rendering parent have stable references.
  4. 4Context changes bypass memo and re-render all consumers. Split contexts by update frequency.
  5. 5useMemo caches computation results but doesn't prevent the component from re-rendering. It prevents downstream work.
  6. 6useTransition makes renders interruptible. Interrupted transitions are discarded — the user never sees half-rendered content.
  7. 7Same-value setState (Object.is match) triggers a bailout. React may still call the component once but won't render children or commit.
  8. 8Always trace the re-render chain: who called setState? → who is the parent of each component? → is memo/composition blocking the cascade?