React Lifecycle & useEffect: Class Methods vs Modern Hooks ??
Ever wondered how React components "live and breathe"? Let's dive into component lifecycles and see how modern hooks have revolutionized the way we handle component life events!
The Big Picture: Class Lifecycle vs useEffect ??
Traditional Class Lifecycle
class DataFetcher extends React.Component {
constructor(props) {
super(props);
this.state = { data: null }; // Setup
}
componentDidMount() {
// When component is born
this.fetchData();
}
componentDidUpdate(prevProps) {
// When component updates
if (prevProps.id !== this.props.id) {
this.fetchData();
}
}
componentWillUnmount() {
// Cleanup when component dies
this.ignore = true;
}
fetchData = async () => {
const result = await fetch(`/api/data/${this.props.id}`);
const data = await result.json();
if (!this.ignore) {
this.setState({ data });
}
};
render() {
return <div>{/* Render data */}</div>;
}
}
Modern Hooks Approach
function DataFetcher({ id }) {
const [data, setData] = useState(null);
useEffect(() => {
let ignore = false;
async function fetchData() {
const result = await fetch(`/api/data/${id}`);
const data = await result.json();
if (!ignore) {
setData(data);
}
}
fetchData();
return () => {
ignore = true; // Cleanup
};
}, [id]); // Dependencies
return <div>{/* Render data */}</div>;
}
Lifecycle Methods vs useEffect: Side-by-Side ??
1. Component Mounting
// Class Method
componentDidMount() {
console.log('Component born');
document.title = this.props.title;
}
// Hook Equivalent
useEffect(() => {
console.log('Component born');
document.title = title;
}, []); // Empty dependency array = componentDidMount
2. Component Updating
// Class Method
componentDidUpdate(prevProps) {
if (prevProps.title !== this.props.title) {
document.title = this.props.title;
}
}
// Hook Equivalent
useEffect(() => {
document.title = title;
}, [title]); // Runs when title changes
3. Component Cleanup
// Class Method
componentWillUnmount() {
window.removeEventListener('resize', this.handleResize);
}
// Hook Equivalent
useEffect(() => {
window.addEventListener('resize', handleResize);
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // Cleanup function in return
Real-World Examples ??
1. WebSocket Connection
领英推荐
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`ws://chat.api/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, event.data]);
};
return () => socket.close(); // Cleanup on unmount or roomId change
}, [roomId]);
return <div>{/* Render messages */}</div>;
}
2. Subscription Management
function UserStatus({ userId }) {
const [isOnline, setIsOnline] = useState(false);
useEffect(() => {
const subscription = subscribeToUserStatus(userId, status => {
setIsOnline(status.online);
});
// Cleanup subscription
return () => subscription.unsubscribe();
}, [userId]); // Resubscribe when userId changes
return <div>User is: {isOnline ? '??' : '?'}</div>;
}
3. Animation Controller
function AnimatedComponent({ isVisible }) {
const elementRef = useRef(null);
useEffect(() => {
if (!elementRef.current) return;
const animation = elementRef.current.animate([
{ opacity: 0 },
{ opacity: 1 }
], {
duration: 1000,
fill: 'forwards'
});
return () => animation.cancel(); // Cleanup animation
}, [isVisible]);
return <div ref={elementRef}>{/* Content */}</div>;
}
Common useEffect Patterns ??
useEffect(() => {
let mounted = true;
async function loadData() {
const data = await fetchData();
if (mounted) {
setData(data);
}
}
loadData();
return () => {
mounted = false;
};
}, [/* dependencies */]);
2. Event Listeners
useEffect(() => {
const handler = (event) => {
// Handle event
};
window.addEventListener('event', handler);
return () => window.removeEventListener('event', handler);
}, []);
3. Intervals/Timers
useEffect(() => {
const interval = setInterval(() => {
// Do something
}, 1000);
return () => clearInterval(interval);
}, []);
Best Practices ??
?? Weekly Challenge
Convert this class component to use hooks:
class ScrollTracker extends React.Component {
state = { scrollY: 0 };
componentDidMount() {
window.addEventListener('scroll', this.handleScroll);
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll);
}
handleScroll = () => {
this.setState({ scrollY: window.scrollY });
};
render() {
return <div>Scrolled: {this.state.scrollY}px</div>;
}
}