Understanding React Component Lifecycle with Hooks
Last updated 2 months, 2 weeks ago | 105 views 75 5

Introduction: Why the React Lifecycle (with Hooks) Matters
Every React component goes through a lifecycle—from mounting to updating and finally unmounting. In class-based components, we used lifecycle methods like componentDidMount
, componentDidUpdate
, and componentWillUnmount
.
But with the rise of functional components and React Hooks, we can now manage the entire lifecycle using Hooks like useEffect
and useLayoutEffect
.
Understanding how to control the lifecycle with hooks is essential for:
-
Fetching data
-
Setting up subscriptions or timers
-
Cleaning up on unmount
-
Optimizing rendering behavior
Let’s break it all down with examples you can actually use in production.
Component Lifecycle Stages (Class vs Functional)
Lifecycle in Class Components
Stage | Class Method | Description |
---|---|---|
Mounting | componentDidMount |
After component is added to the DOM |
Updating | componentDidUpdate |
On state/props change |
Unmounting | componentWillUnmount |
Before component is removed |
Lifecycle in Functional Components (with Hooks)
Stage | Hook Used | Equivalent to |
---|---|---|
Mounting | useEffect(() => {}, []) |
componentDidMount |
Updating | useEffect(() => {}, [deps]) |
componentDidUpdate |
Unmounting | return () => {} inside useEffect |
componentWillUnmount |
Managing Lifecycle with useEffect
✅ 1. useEffect
on Mount (componentDidMount)
import React, { useEffect } from 'react';
function HelloComponent() {
useEffect(() => {
console.log('Component mounted!'); // Called once
}, []);
return <h1>Hello World</h1>;
}
Passing an empty dependency array
[]
makes it run once on mount.
✅ 2. useEffect
on Update (componentDidUpdate)
useEffect(() => {
console.log('Count changed:', count);
}, [count]); // Runs whenever `count` changes
You can track specific variables by including them in the dependencies array.
✅ 3. Cleanup on Unmount (componentWillUnmount)
useEffect(() => {
const timer = setInterval(() => console.log('Tick'), 1000);
return () => {
clearInterval(timer); // Cleanup
console.log('Component unmounted');
};
}, []);
The cleanup function runs when the component is removed or before next effect run.
useLayoutEffect
vs useEffect
Feature | useEffect |
useLayoutEffect |
---|---|---|
Timing | Runs after DOM painting | Runs before DOM painting |
Use case | Data fetching, subscriptions | DOM measurements, layout adjustments |
Performance | Non-blocking | Blocking |
Example:
useLayoutEffect(() => {
// Runs before the browser paints
}, []);
⚠️ Use
useLayoutEffect
only when necessary to avoid layout thrashing.
useEffect Lifecycle Simulation Summary
Lifecycle Stage | Hook Usage |
---|---|
On Mount | useEffect(() => {}, []) |
On Update | useEffect(() => {}, [dependencies]) |
On Unmount | useEffect(() => { return () => {} }, []) |
✅ Full Functional Example: Counter with Mount/Update/Unmount Logs
import React, { useEffect, useState } from 'react';
function Counter() {
const [count, setCount] = useState(0);
// On Mount
useEffect(() => {
console.log('Component Mounted');
return () => {
console.log('Component Unmounted');
};
}, []);
// On Update
useEffect(() => {
if (count > 0) {
console.log('Count Updated:', count);
}
}, [count]);
return (
<div>
<h2>Counter: {count}</h2>
<button onClick={() => setCount(count + 1)}>Increment</button>
</div>
);
}
export default Counter;
Try this: Open your browser console to see logs as the component mounts, updates, and unmounts.
Tips & Common Pitfalls
✅ Best Practices
-
Use
useEffect(() => {}, [])
for fetching data on mount. -
Clean up inside the
useEffect
return function (e.g., event listeners, timers). -
Avoid unnecessary re-renders by limiting dependencies in the effect array.
-
Use custom hooks for reusable lifecycle logic.
❌ Common Pitfalls
Mistake | Problem | Fix |
---|---|---|
Omitting dependencies in useEffect |
Stale data or bugs | Add all dependencies explicitly |
Using useEffect for layout calc |
Flickering UI | Use useLayoutEffect instead |
Forgetting cleanup on unmount | Memory leaks or duplicate listeners | Always return cleanup in useEffect |
Quick Comparison: Class vs Hook Lifecycle
Behavior | Class Component | Hook Equivalent |
---|---|---|
On mount | componentDidMount() |
useEffect(() => {}, []) |
On update | componentDidUpdate() |
useEffect(() => {}, [value]) |
On unmount | componentWillUnmount() |
return () => {} inside useEffect |
Conclusion: Hooks Make Lifecycle Easier and Cleaner
With React Hooks, managing the component lifecycle is simpler, more readable, and more powerful than ever before. Whether you're fetching data, setting up a subscription, or cleaning up resources, useEffect
and useLayoutEffect
give you full control without class-based boilerplate.
Key Takeaways:
Replace class lifecycle methods with
useEffect
.Always clean up side effects.
Choose
useLayoutEffect
only when DOM layout matters.Break complex logic into custom hooks for reuse and clarity.