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.
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:
In the functional world, we don't have componentDidMount or componentWillUnmount. Instead, we use useEffect to synchronize our components with these lifecycle moments.
useEffect(() => { ... }, []). The empty dependency array tells React to run this effect exactly once after the initial render.useEffect(() => { ... }, [dependencies]). The effect runs whenever any value in the array changes.useEffect. If your effect returns a function, React runs that function right before the component is removed from the DOM.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>; }
It's common to assume that the code inside your component function runs after the DOM is updated. That's not entirely true.
useEffect hooks.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.
Open your project and navigate to your MovieCard component.
useEffect that logs "Card rendered" to the console.useEffect that logs "Card removed".MovieCard in a conditional check (e.g., showCard && <MovieCard />).showCard state and observe the console. Does the cleanup function run when you hide the card?setState inside the effect.useEffect is asynchronous. The UI is updated before the effect runs. If you try to read DOM measurements (like div.offsetWidth) inside the effect, you are safe; doing it in the render body is unreliable.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.
Master React state management by understanding when to use local state, custom hooks, or Context. Learn best practices to build maintainable, scalable UIs.
Read moreLearn to build an interactive search bar in React. Master controlled inputs by synchronizing form values with local state for a reactive, responsive UI.
Review of Component Lifecycle
Working with LocalStorage
Building a Favorites List
Handling Media in React
Introduction to Testing
Debugging React Apps
Deployment Basics
Using External Libraries
Advanced