synchronization, data fetching, subscriptions. The bridge between React and the outside world.
Always place side effects inside useEffect. Never run them directly in the component body.
import { useEffect, useState } from 'react';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
useEffect(() => {
// This runs after current render
fetch(`/api/user/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]); // Only re-run if 'userId' changes
if (!user) return <div>Loading...</div>;
return <div><h1>{user.name}</h1></div>;
}If your effect creates a subscription or timer, you MUST clean it up to avoid memory leaks. Return a function to do this.
useEffect(() => {
const timerId = setInterval(() => {
console.log('Tick!');
}, 1000);
// Cleanup function
return () => {
clearInterval(timerId); // Stop the timer!
console.log('Cleaned up');
};
}, []); // Empty array = run once on mount