When I first started with React, everything revolved around class components. They had lifecycle methods (componentDidMount, componentDidUpdate, componentWillUnmount) and state management inside a class.
Then came Hooks (introduced in React 16.8). At first, I was skeptical — but after migrating real apps, I saw the power they brought. Here’s what I learned.
import React, { Component } from "react";
class Counter extends Component {
state = { count: 0 };
componentDidMount() {
console.log("Mounted!");
}
componentDidUpdate() {
console.log("Updated!");
}
componentWillUnmount() {
console.log("Unmounting!");
}
render() {
return (
Count: {this.state.count}
);
}
}
State lives in the class.
Lifecycle methods handle mounting, updating, unmounting.
Verbose and harder to reuse.
import React, { useState, useEffect } from "react";
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
console.log("Mounted or Updated!");
return () => console.log("Unmounting!");
}, [count]);
return (
Count: {count}
);
}
useState manages state.
useEffect combines lifecycle methods.
Code is shorter, cleaner, and easier to test.
Less Boilerplate
Hooks cut down code size. No more binding this .
Reusability
With custom hooks, I could extract logic and reuse it across components. That wasn’t possible with class lifecycles.
Incremental Migration
We didn’t have to rewrite everything at once. Hooks worked side by side with class components.
Pitfalls
Forgetting dependency arrays in useEffect caused weird bugs.
Overusing hooks in one component made things messy (needed splitting).
Better Developer Experience
Once the team got used to hooks, onboarding new devs became easier — no need to explain lifecycle methods.
Looking back, Hooks were one of the biggest shifts in my frontend career. They made React feel more natural and composable.
Today, I rarely write class components. But going through that migration taught me a valuable lesson: the ecosystem changes, and adapting early pays off.