- React
- Tricky React Output Prediction & Execution Ordering
- Output Traps: Effect Order, Cleanups & StrictMode
Output Traps: Effect Order, Cleanups & StrictMode
In a parent-child React tree, render phases execute top-down (parent before child), but commit passive effects execute bottom-up (children effects run before parent effects). When dependencies change, previous effect cleanups run before new effects execute.
Key Learning Objectives
Predict the exact execution order of useEffect across nested parent-child component trees.
Explain why child effects run BEFORE parent effects during the commit passive effects phase.
Analyze cleanup timing: previous effect cleanup runs before subsequent effect invocation.
Explain why React 18 StrictMode runs effects twice on initial mount in development.
The Interview Problem & Context
Predict the exact console output when a Parent component containing a Child component mounts, and then the Child unmounts.
1// Simulating React component tree effect order2function simulateEffectLifecycle() {3 const log: string[] = [];45 // Mount Phase:6 // Render phase goes top-down: Parent renders -> Child renders7 // Commit passive effects phase goes bottom-up (post-order):8 // 1. Child useEffect runs first!9 log.push('child-effect');1011 // 2. Parent useEffect runs second!12 log.push('parent-effect');1314 // Unmount / Dependency Change Phase:15 // 3. Child cleanup runs when unmounted or before re-running!16 log.push('child-cleanup');1718 return log.join(' ');19}2021console.log(simulateEffectLifecycle());
Predict Console & Render Output
child-effect parent-effect child-cleanup
parent-effect child-effect child-cleanup
child-cleanup child-effect parent-effect
parent-effect child-cleanup child-effect
Execution Trace
Step 1 of 3 (Line 9)In post-order commit traversal, Child useEffect runs before Parent useEffect.
Deep Architectural Explanation
Why Child Effects Run Before Parent Effects
The execution of a React component tree happens in two distinct directional passes:
1. Render Phase (Top-Down Pre-Order):
- React evaluates the root/parent component first.
- The parent returns JSX containing children.
- React descends into children to call their component functions.
2. Passive Effects Phase (Bottom-Up Post-Order):
- When the commit phase finishes mutating the DOM, React walks the Fiber effect list.
- React processes children effects first, then ascends to the parent.
- Why this design? A parent component frequently depends on its children being fully mounted and initialized (e.g. measuring child DOM nodes or listening to child events). If parent effects ran first, child DOM nodes would not yet be ready!
3. Cleanup Timing:
- On re-render:
cleanup(oldProps)runs beforeeffect(newProps). - On unmount:
cleanup()runs immediately when the component leaves the tree.
Common Interview Traps & Pitfalls
Assuming parent effects execute before child effects because the parent renders first.
Expecting effect cleanups to run after the next effect completes.
Forgetting that React 18 StrictMode in development mounts, unmounts, and remounts immediately.
Senior Interviewer Follow-Up Probes
How does `useEffectEvent` (experimental in React) decouple reactive dependencies from effect execution?
What happens if an error is thrown inside an effect cleanup function?
How does React schedule passive effects using MessageChannel / postMessage under the hood?
Frequently Asked Interview Questions
When does the useEffect cleanup function execute?
When the component unmounts, and before re-executing the effect when dependencies change on subsequent renders.
Why does React 18 StrictMode mount components twice in development?
To test that components handle unmounting and remounting cleanly, exposing missing cleanup functions for event listeners, subscriptions, and timers.
Do useLayoutEffect callbacks run before or after useEffect?
useLayoutEffect runs synchronously immediately after DOM mutations before browser paint. useEffect runs asynchronously after the browser paints the screen.
Can you return a Promise from a useEffect callback?
No. useEffect callbacks must either return a cleanup function or undefined. Returning a Promise causes an error because React expects a cleanup function.
What is the effect dependency comparison algorithm in React?
React compares each item in the dependency array using Object.is equality with the value from the previous render pass.
