Review of Component Lifecycle: Mastering React Internals
Master the React lifecycle to write more predictable code. Learn how to map component mounting, updating, and unmounting phases to the hooks you use daily.
Previously in this course, we explored how to handle asynchronous data and side effects in fetching data from an API and cleanup functions in useEffect. Now that we’ve built complex features like debounced search and custom hooks, it’s time to zoom out and look at the "big picture" of how React manages a component's existence.
Understanding the component lifecycle is the difference between writing "code that works" and writing "code that is robust." When you grasp the order of operations, you stop guessing why a state update triggered a re-render or why your API call fired twice.
The Three Phases of the Lifecycle
Every component in React moves through three distinct phases. While modern functional components use hooks rather than the old class-based methods, the underlying mechanics remain the same:
- Mounting: The component is being born. React creates the component instance, runs your function body, and inserts the resulting DOM nodes into the browser.
- Updating: The component is growing or changing. This happens when props change, state is updated, or the parent component re-renders.
- Unmounting: The component is being destroyed. React removes the component from the DOM tree, and it's time to clean up any lingering resources.
Mapping Lifecycle Phases to Hooks
In the functional world, we don't have componentDidMount or componentWillUnmount. Instead, we use useEffect to synchronize our components with these lifecycle moments.
- Mounting: Represented by
useEffect(() => { ... }, []). The empty dependency array tells React to run this effect exactly once after the initial render. - Updating: Represented by
useEffect(() => { ... }, [dependencies]). The effect runs whenever any value in the array changes. - Unmounting: Represented by the function returned inside
useEffect. If your effect returns a function, React runs that function right before the component is removed from the DOM.
Worked Example: Tracking the Lifecycle
Let’s look at a simple MovieDetail component to see these phases in action. We want to log when the component mounts, when it receives a new movie ID, and when it disappears.
JSXimport { useEffect } from CE9178">'react'; function MovieDetail({ movieId }) { useEffect(() => { // 1. Mounting phase console.log("MovieDetail component mounted!"); // 3. Unmounting phase(Cleanup) return () => { console.log("MovieDetail component unmounted!"); }; }, []); // Empty array = mount only useEffect(() => { // 2. Updating phase console.log(CE9178">`MovieDetail updated for movie: ${movieId}`); }, [movieId]); // Runs when movieId changes return <div>Viewing details for movie {movieId}</div>; }
The Order of Operations
It's common to assume that the code inside your component function runs after the DOM is updated. That's not entirely true.
- Render Phase: React calls your function component. This is pure JavaScript logic. React calculates what the UI should look like.
- Commit Phase: React applies the changes to the real DOM.
- Layout/Effect Phase: Once the browser has painted the screen, React runs your
useEffecthooks.
This separation is why we never trigger state updates directly in the component body—that would cause an infinite loop during the Render Phase. We always delegate side effects to the Effect Phase. For a deeper dive into controlling this flow, revisit Mastering useEffect Dependencies: Control Your React Lifecycle.
Hands-on Exercise
Open your project and navigate to your MovieCard component.
- Add a
useEffectthat logs"Card rendered"to the console. - Add a cleanup function to that same
useEffectthat logs"Card removed". - Wrap the
MovieCardin a conditional check (e.g.,showCard && <MovieCard />). - Toggle the
showCardstate and observe the console. Does the cleanup function run when you hide the card?
Common Pitfalls
- Forgetting the Dependency Array: If you omit the array, your effect runs on every render, which often leads to infinite loops if you are calling
setStateinside the effect. - Assuming Synchronicity: Remember that
useEffectis asynchronous. The UI is updated before the effect runs. If you try to read DOM measurements (likediv.offsetWidth) inside the effect, you are safe; doing it in the render body is unreliable. - Not Cleaning Up: Failing to return a cleanup function for event listeners or intervals is a leading cause of memory leaks. As we discussed in Cleanup Functions in useEffect: Preventing Memory Leaks, always clean up what you create.
Recap
The React lifecycle is a sequence of events: mounting, updating, and unmounting. We use useEffect to tap into these moments. By keeping your render logic pure and moving side effects into the appropriate lifecycle hooks, you ensure your components are performant and easy to debug.
Up next: We'll dive into the nuances of state management and how to choose the right strategy for complex applications.
Work with me

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.

Headless WordPress + Next.js Frontend Development
Keep WordPress for content, get a lightning-fast Next.js frontend. The best of both worlds — familiar editing, modern speed.