React Hooks (introduced in 16.8) are now the standard. Class components with `this.setState` and lifecycle methods are legacy. This guide provides direct translation patterns for migrating typical class components to functional components with Hooks.
State Management: useState
// Class
class Counter extends React.Component {
state = { count: 0, text: '' };
render() {
return <div>{this.state.count}</div>;
}
}
// Hooks
function Counter() {
const [count, setCount] = useState(0);
const [text, setText] = useState(''); // Split state for better granularity
return <div>{count}</div>;
}
Lifecycle: useEffect
`useEffect` replaces `componentDidMount`, `componentDidUpdate`, and `componentWillUnmount`.
// Class
componentDidMount() {
API.subscribe(this.props.id);
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id) {
API.unsubscribe(prevProps.id);
API.subscribe(this.props.id);
}
}
componentWillUnmount() {
API.unsubscribe(this.props.id);
}
// Hooks
useEffect(() => {
API.subscribe(props.id);
// Cleanup function (Unmount or before re-run)
return () => API.unsubscribe(props.id);
}, [props.id]); // Dependency array acts as the conditional check
Common Pitfall: Stale Closures
Hooks capture the state *at the time of render*. If you use `setInterval`, you might see old state.
// ❌ Broken: Always prints 0
useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(id);
}, []); // Empty deps means 'mount only'
// ✅ Fix: Use functional state update
useEffect(() => {
const id = setInterval(() => {
setCount(c => c + 1); // Access latest value
}, 1000);
return () => clearInterval(id);
}, []);
Key Takeaways
- Hooks encapsulate logic, not lifecycle phases.
- Ensure dependency arrays in `useEffect` are exhaustive (use the ESLint plugin).
- Don’t rewrite everything at once; Classes and Hooks coexist fine.
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.