What you are practising: useEffect, modelling a request with a discriminated union, fetching data through a typed API client, and (guided, in class) a controlled form.
Accept the assignment, clone your repository, then install once:
npm installThis session runs two processes side by side: the mock API server and the Vite dev server.
# Terminal 1
npm run api
# Terminal 2
npm run devThe API serves the destinations at http://localhost:3001/destinations; the app runs at http://localhost:5173. If the app loads but every request fails, the most likely cause is that the API terminal is not running.
Offline fallback: if you cannot reach the API at all, set USE_MOCK = true at the top of src/api.ts and everything works against built-in mock data instead.
Reset your data: the API writes to db.json. If you mangle it while experimenting, npm run reset-db restores it.
src/api.ts: the typed API client, fully implemented. Read it; you consume it, you do not change it.db.json: the ten destinations, now with phases. This is the API server's data file.src/components/Card.tsx: the<Card>carried forward from earlier sessions.src/components/DestinationList.tsx: a shell component with TODOs. This is your independent exercise.src/components/AddDestinationForm.tsx: a shell for the guided exercise we build together in class.
In src/components/DestinationList.tsx:
- Use
useEffectto callapi.listDestinations()once on mount. - Model the request state as a discriminated union (
idle | loading | success | error) held in a singleuseState. - Render a different UI for each branch.
- On success, render the list using
<Card>and the rendering work from Sessions 5 and 6. - Add a "Refresh" button that re-runs the fetch.
- Add an
AbortControllerso that unmounting the component cancels the in-flight fetch. - Add a controlled search input that filters the displayed destinations by name (client-side; no new API call).
- If the fetch fails, show a retry button that returns the state to loading and re-runs the effect.
The list of ten destinations loads from the API with a visible loading state on the way, stopping the API server and refreshing shows your error branch instead of a blank page, the refresh button re-fetches, npm run typecheck passes, and your work is committed and pushed.
Every push runs two GitHub Actions checks:
- Type check — runs
tsc -bon your project. - Autograding — renders your
<App />with React Testing Library and a mocked network, then checks: a loading state shows before the data, the destinations render on success, an error state shows when the fetch fails, and the Refresh button triggers another fetch — plus an automated code-quality review. Use the word "Loading" for the loading branch and label the Refresh button "Refresh" so the grader can find them.