Fullstack CourseLearn by building
Back to roadmap

Week 2 — Managing State, Escape Hatches & Data

Days 1–2 build React state and Effects fluency; days 3–5 apply Redux Toolkit and TanStack Query to Nest-backed data — then ship a Todo mini-project (RTK + Query).

End-of-week mini-project · practice only

Todo app (RTK + Query)

Ship a Todo UI where unfinished draft/filter state lives in Redux Toolkit and the todo list comes from TanStack Query, with debounced search and memoized rows.

Definition

A client that separates unfinished composer/chrome state (RTK) from server-backed todo rows (TanStack Query + Axios cookies), applying the Week 2 escape hatches — reducers, refs, Effects, custom hooks, and memoization — with the course ownership rule.

In simpler words

Type a draft in Redux. When you save, Query talks to Nest (or a mock). Never store the Nest list in Redux, and don’t refetch on every keystroke.

Concepts covered this week

Reacting to input with state
A controlled composer input drives the new-todo draft.
Choosing the state structure
Store only draft, filter, and selection — derive the visible list instead of duplicating it.
Sharing & lifting state
The toolbar filter and the list read the same shared state.
Preserving & resetting state
Editing a todo remounts the form via a key so stale text does not leak in.
Extracting logic into a reducer
createSlice reducers own the draft/selection/filter transitions.
Context & scaling reducer + context
The RTK Provider replaces hand-rolled context; a tiny theme context shows the pattern it improves on.
Referencing values with refs
A ref focuses the input after add; another ref holds a debounce timer id that must not cause renders.
Manipulating the DOM with refs
A DOM ref scrolls a newly added todo into view.
Synchronizing with Effects / you might not need an Effect
The filtered list is derived in render; an Effect is used only for a real subscription like online status.
Effect lifecycle & removing dependencies
A debounced-search Effect cleans up its timer and lists honest dependencies.
Separating events from Effects
Saving a todo is a click handler, not an Effect reacting to state.
Reusing logic with custom Hooks
useDebouncedValue and useOnlineStatus package the Effect logic.
Caching with useMemo / useCallback
Memoize the filtered list and pass stable handlers to memoized rows.
Redux Toolkit & client drafts
configureStore + a slice own draft text, selected id, and filter chrome.
TanStack Query fundamentals
useQuery loads the list; useMutation creates/toggles/deletes and invalidates.
Axios, Nest & cookies
Axios withCredentials sends the cookie JWT; no token in localStorage.
RTK Query vs TanStack Query
Apply the ownership rule: server rows in Query, unfinished UI state in RTK.

Deliverables

  • configureStore + createSlice for draft text, selected id, and filter chrome
  • Typed useAppDispatch/useAppSelector; RTK Provider on the client tree
  • useQuery list + useMutation create/toggle/delete with invalidateQueries on success
  • A useDebouncedValue custom hook so search does not refetch every keystroke; memoized filtered list + rows
  • Axios withCredentials for cookie JWT; honest loading/empty/error UI with submit disabled while pending

Build guide

  1. Model client stateCreate a slice for draft text, selection, and filter; derive the visible list rather than storing it.
  2. Wire the storeAdd configureStore, typed hooks, and the Provider on the client tree; watch draft updates in Redux DevTools.
  3. Load the list with QueryFetch todos with useQuery through an Axios client that sends credentials.
  4. Mutate + invalidatecreate/toggle/delete via useMutation; on success clear the draft and invalidateQueries.
  5. Search without over-fetchingExtract useDebouncedValue; keep filterDraft in RTK and only apply it after the debounce.
  6. Optimize rendersMemoize the filtered list and row handlers so typing does not re-render every row.
  7. Handle the edgesShow distinct loading/empty/error states and disable submit while a mutation is pending.

Acceptance checklist

- [ ] RTK owns draft + selection + filter (not the server todos array)
- [ ] Query owns the list; onSuccess clears draft + invalidateQueries
- [ ] Search is debounced via a custom hook; typing does not refetch each keystroke
- [ ] Filtered list + row handlers are memoized
- [ ] Cookie credentials path works (or documented stub)
- [ ] Empty ≠ loading; mutation errors visible on the form; no JWT in localStorage

Stretch goals

  • Add an optimistic toggle with rollback on mutation error.
  • Persist the filter/selection to the URL query string.
  • Add drag-to-reorder using a ref-backed pointer handler.

Tips

  • Open /examples/data for the live Query + Axios lab; skim /examples/state for the RTK-vs-Query ownership table.
  • If the Nest Todos API is not ready, mock the HTTP contract first, then swap the queryFn.
  • Reuse the same ownership table you will need for the Week 4 Notes UI.
  • Typing should update filterDraft (RTK), not appliedFilter — only the debounced value refetches.