What useEffect is even for
useEffect runs code after React has already updated the DOM. It's the go-to spot for fetching data, setting up a subscription, or syncing with something outside React entirely - analytics, localStorage, that sort of thing.
The dependency array decides when it re-runs
- Leave it out entirely → the effect runs after every single render
- Pass an empty array
[]→ runs exactly once, right after the first render - List some values → it re-runs whenever any of those values change
Fetching data when the component mounts
import { useEffect, useState } from "react";
function ArticleList() {
const [articles, setArticles] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/articles")
.then((res) => res.json())
.then((data) => setArticles(data))
.finally(() => setLoading(false));
}, []); // empty array = run once on mount
if (loading) return <p>Loading...</p>;
return <ul>{articles.map((a) => <li key={a._id}>{a.title}</li>)}</ul>;
}
Cleaning up after yourself
Return a function from the effect and React will call it right before the effect runs again, and again when the component unmounts. This is how you avoid leaking subscriptions or in-flight requests.
useEffect(() => {
const controller = new AbortController();
fetch("/api/articles", { signal: controller.signal })
.then((res) => res.json())
.then(setArticles)
.catch(() => {}); // ignore abort errors
return () => controller.abort(); // cleanup
}, []);
The mistake almost everyone makes
Forgetting to list a value inside the dependency array even though it's used inside the effect. ESLint's react-hooks/exhaustive-deps rule will catch this for you - leave it turned on, it's not being annoying, it's saving you a bug.
Comments
Loading comments...
Related Articles
Taming Slow MongoDB Aggregation Pipelines in Production
An aggregation pipeline that returns instantly on your 500-document local dataset can crawl once it hits a few million real documents. Here's how to actually find and fix the stage that's costing you.
Read ArticleBuilding a Sliding-Window Rate Limiter for Node.js APIs with Redis
Fixed-window rate limiting looks fine in a demo and then lets through double the traffic right at the window boundary. Here's the sliding-window-counter approach that fixes that, built directly on Redis.
Read ArticleMulti-Tenant Data Isolation: Shared Schema vs Separate Databases
Every multi-tenant SaaS eventually has to answer one question honestly: how do you stop one customer from ever seeing another customer's data? Here's how the three common isolation strategies actually compare in practice.
Read Article