Lifecycle vs useEffect
useEffectreplaces most lifecycle methods related to side effects, but is not a full replacement for all class capabilities. Error Boundaries are a key exception, which are still only implemented via classes.
Class lifecycle (React < = 16)
- Logic is split between phases:
componentDidMountcomponentDidUpdatecomponentWillUnmount
- Side effect often split between multiple methods
- Manual
prevProps / prevStatechecks required - Rigid phase model
- Support for Error Boundaries
componentDidCatch(error, info)
static getDerivedStateFromError(error)Classes are the only way to catch rendering errors in child components.
useEffect (React 16.8+)
- Single API for side effects
- Behavior controlled by dependency array
- Cleanup described declaratively
- Logic easily extracted into custom hooks
- Does not catch rendering errors
useEffect(() => {
subscribe();
return () => unsubscribe();
}, [id]);useEffect does not run if the render fails with an error.
Error Boundaries — Fundamental Difference
-
Error Boundary:
- catches errors during render / commit phases
- isolates UI crashes
-
useEffect: - runs after a successful commit - cannot be used for error recovery Therefore Error Boundaries cannot be implemented via hooks.
Conceptual Difference
- Class lifecycle → phase-based, imperative model + error interception
- useEffect → declarative, data-driven model without rendering error control
Practical modern pattern
-
Business logic, data fetching, subscriptions → hooks
-
Error handling UI → class-based Error Boundary
-
In real-world projects:
- classes are used almost exclusively for Error Boundaries
Short Answer for Interview
useEffect replaces lifecycle methods for side effects but does not replace Error Boundaries.
Catching rendering errors is only possible via class components, so classes are still necessary in modern React.