Frontend Master

JavaScript Curriculum

Data Fetching Patterns
+60 XP

Data Fetching Patterns

medium
~30 min·60 XP

The coffee shop menu needs to load items from a server, show a skeleton while loading, show an error with a retry button if it fails, and an empty state if there are no specials. These are the four states every data-fetching component must handle.

Data Fetching Patterns

Every component that loads remote data follows the same pattern: useState for data, loading, and error. useEffect to trigger the fetch. AbortController to cancel on unmount. Extract it into useFetch and reuse it everywhere.

Four Fetch Patterns

Basic, abort cleanup, parallel, and mutations:

fetch-patterns.jsx

The foundational pattern: fetch in useEffect, store in state, handle loading and error. Everything else builds on this.

function MenuList() {
  const [items, setItems]     = useState([])
  const [loading, setLoading] = useState(true)
  const [error, setError]     = useState(null)

  useEffect(() => {
    fetch('/api/menu')
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`)
        return res.json()
      })
      .then(data => { setItems(data.items); setLoading(false) })
      .catch(err => { setError(err.message); setLoading(false) })
  }, [])

  if (loading) return <Spinner />
  if (error)   return <ErrorCard message={error} />
  return <ul>{items.map(i => <MenuCard key={i.id} {...i} />)}</ul>
}
Simulate
loading:false
error:null
data: null
ℹ️The three state variables
Every data-fetching component needs exactly three state variables: `data` (starts null), `loading` (starts true), `error` (starts null). Use early returns to handle each state: `if (loading) return <Spinner />`, `if (error) return <Error />`. The main render only runs when data is ready.

Loading, Error, Empty States — All Four

See each UI state with realistic content:

<loaderrorDemo></loaderrorDemo>

💡Tip
Spinner for lists"> Skeleton loaders show the shape of the content before it arrives — the brain fills in the gaps and the perceived load time is lower. Use skeletons for list items and cards. Use spinners for full-page loading or button-level feedback.

Build and Test useFetch Live

The complete reusable hook — test it against different endpoints:

useFetch-builder.jsx
// useFetch — your reusable data-fetching hook
function useFetch(url) {
  const [data, setData]       = useState(null)
  const [loading, setLoading] = useState(true)
  const [error, setError]     = useState(null)

  useEffect(() => {
    const controller = new AbortController()
    setLoading(true); setError(null)

    fetch(url, { signal: controller.signal })
      .then(res => {
        if (!res.ok) throw new Error(`HTTP ${res.status}`)
        return res.json()
      })
      .then(d => { setData(d); setLoading(false) })
      .catch(e => {
        if (e.name !== 'AbortError') {
          setError(e.message); setLoading(false)
        }
      })

    return () => controller.abort()
  }, [url])

  return { data, loading, error }
}

// Usage — any component, any URL:
const { data, loading, error } = useFetch('/api/menu')
const { data: user }           = useFetch('/api/user')
Test useFetch live
Endpoint
loading:false
error:null
data: null
⚠️Always abort on cleanup
Without `controller.abort()` in the cleanup function, if the user navigates away while a fetch is in progress, the fetch completes and tries to call `setState` on an unmounted component. React 18 removed the warning but it is still a bug — the component may re-mount with stale data.

Your Challenge

Build useFetch(url) returning { data, loading, error }. Build MenuList using it — skeleton while loading, error card with retry button, empty state, then data grid. Build OrderButton — POST on click, disabled while submitting, shows "✓ Ordered!" for 2 seconds then resets to "Order Now".

Challenge

Build a useFetch(url) hook. Build MenuList that uses it — four states: loading skeleton, error with retry, empty state, data grid. Build an OrderButton that POST-fetches on click — disabled while loading, shows success/error feedback.

fetchuseEffectloading-stateerror-stateAbortControllerparallel-fetchPromise.allPOSTuseFetchmutationsoptimistic-update
Data Fetching Patterns | Nexus Learn