Fetching server data with useEffect and useState — a loading flag, an error flag, manual cleanup — works for trivial cases but quickly becomes a source of bugs: race conditions, duplicate requests, no caching, stale data, and refetch logic scattered everywhere. Server data is fundamentally different from local UI state, and dedicated data-fetching libraries exist to manage it properly.
Two libraries dominate: SWR and TanStack Query (formerly React Query). Both treat server data as a cache you keep in sync, giving you caching, automatic deduplication, background revalidation, and clean loading and error states out of a single hook. They turn pages of effect-and-state plumbing into a declarative call describing what data you want.
The key insight is the distinction between server state (data that lives on the server, which you cache and synchronise) and client state (UI state you own). This lesson covers why dedicated libraries beat hand-rolled fetching, how SWR and TanStack Query work, querying and mutating data, and cache invalidation — the patterns that make data-heavy React apps robust.