Engineering2024-05-10 · 8 min read
Stop using useEffect for data fetching
A deep dive into Server Actions and why the client boundary matters.
Stop using useEffect for data fetching
If you are a React developer, you might be familiar with the pattern of fetching data using useEffect. However, in Next.js App Router, there is a better way.
Problems with the existing approach
// ❌ Avoid this pattern now
export default function Page() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/data')
.then(res => res.json())
.then(data => {
setData(data);
setLoading(false);
});
}, []);
if (loading) return <Skeleton />;
return <Content data={data} />;
}
Disadvantages of this approach:
- Waterfall requests: Data fetching starts only after the component renders.
- JavaScript required: Bad for SEO.
- Loading state management: Increased boilerplate code.
Solving with Server Components
// ✅ Recommended approach in App Router
export default async function Page() {
const data = await fetchData(); // Runs on the server
return <Content data={data} />;
}
Understanding the Client Boundary
The 'use client' directive doesn't just mean "run on the client". It defines a boundary.
Conclusion
If you use Next.js App Router, switch to a server-first mindset. Handle most data fetching on the server and focus the client only on interactions.