Understanding React Component Lifecycle with Hooks
Last updated 4 months, 1 week ago | 304 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
useLayoutEffectonly 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
useEffectreturn 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
useLayoutEffectonly when DOM layout matters.Break complex logic into custom hooks for reuse and clarity.