Checkpoint: A Mini App

You've learned lifting state up , Context , useReducer , data fetching and state libraries — let's build something real. This is a checkpoint, not a tutorial: you'll combine those skills into one small but complete feature, then test your recall with a quiz. No new APIs here. Just the satisfying click of everything fitting together.

Learn Checkpoint: A Mini App in our free React course — a beginner-friendly interactive lesson with runnable examples, a practice exercise and a quick recall.

Part of the free React course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Before you build, here's the toolkit you're bringing to this checkpoint. If any of these feel shaky, the linked lesson is one click away.

Now try the logic yourself below. Fill in the three blanks so search transitions through its states and remembers recent queries:

Here's the full React implementation. Read it top-to-bottom — it's exactly the component tree above, wired together.

Why this shape? The search result is local to App because only it needs the user object. The recent list is shared via Context because both the list and (later) any sidebar could read it. The reducer keeps the four async states impossible to get into a contradictory combination — you can't be both "loading" and "error" at once.

Six questions covering the state & data lessons that led here. Answer in your head, then reveal.

1) Two sibling components both need the same search results. Where should that state live?

In their closest common parent — that's lifting state up . The parent holds the state and passes it (plus a setter) down to both siblings as props. If many distant components need it, reach for Context instead.

2) Why use useReducer for the fetch status instead of three separate useState calls?

Because the fields move together. With separate states you can accidentally land in impossible combinations (loading and error). A reducer makes each transition a single, named action, so the state stays consistent.

3) What's the difference between Context and lifting state up?

Both share state, but lifting passes it explicitly through props — fine for a level or two. Context broadcasts it to any descendant without prop-drilling — better when many components, deep in the tree, need the same value.

4) In the data-fetching effect, why include a cleanup that sets an ignore flag?

To avoid setting state after the component unmounts or after a newer request supersedes this one. The cleanup runs before the next effect (or on unmount), flips ignore to true, and the stale .then checks it before calling setState .

5) The recent-searches Context value is {' '} . Why wrap addRecent in useCallback ?

So the function keeps a stable identity across renders. Otherwise the context value object changes every render, forcing every consumer to re-render and breaking any useEffect that depends on addRecent .

6) When would you reach for Zustand or Redux Toolkit instead of Context here?

When the shared state updates frequently and is read by many components, Context can cause broad re-renders. A store with selectors (Zustand) or slices + memoized selectors (Redux Toolkit) lets components subscribe to just the slice they use — and lets you update state from outside React.

Practice quiz

Two sibling components both need the same search results. Where should that state live?

  • Duplicated in each sibling
  • In a module-level global variable
  • In their closest common parent (lifting state up)
  • In useRef inside one sibling

Answer: In their closest common parent (lifting state up). Lifting state up puts shared state in the closest common parent, which passes it (and a setter) down to both siblings as props.

Why use one useReducer for the fetch status instead of three separate useState calls?

  • The fields move together, so a reducer prevents impossible combinations like loading and error at once
  • Reducers are faster than useState
  • useState cannot hold strings
  • It avoids using Context

Answer: The fields move together, so a reducer prevents impossible combinations like loading and error at once. A reducer makes each transition a single named action, keeping related fields consistent and ruling out contradictory states.

What is the difference between Context and lifting state up?

  • They are the same thing
  • Context only works for theme values
  • Lifting state up requires a reducer
  • Lifting passes state explicitly through props; Context broadcasts it to any descendant without prop drilling

Answer: Lifting passes state explicitly through props; Context broadcasts it to any descendant without prop drilling. Lifting is explicit prop passing, fine for a level or two. Context broadcasts to deep descendants without threading props through every layer.

In a data-fetching effect, why include a cleanup that sets an 'ignore' flag?

  • To cancel the network connection immediately
  • To avoid setting state after unmount or after a newer request supersedes this one
  • To memoize the fetch result
  • To re-run the effect more often

Answer: To avoid setting state after unmount or after a newer request supersedes this one. The cleanup flips ignore to true before the next effect or on unmount, so a stale .then checks it and skips setState, avoiding the unmounted-update warning.

The recent-searches Context value is { recent, addRecent }. Why wrap addRecent in useCallback?

  • To give it a stable identity so the context value doesn't change every render and force consumers to re-render
  • To make addRecent run faster
  • Because Context requires useCallback
  • To prevent addRecent from being called twice

Answer: To give it a stable identity so the context value doesn't change every render and force consumers to re-render. A stable function identity keeps the context value object from changing each render, which would otherwise re-render every consumer and break effects depending on addRecent.

When would you reach for Zustand or Redux Toolkit instead of Context here?

  • When the app has only one component
  • Whenever you fetch data
  • When shared state updates frequently and is read by many components, where selectors avoid broad re-renders
  • Never; Context always scales better

Answer: When shared state updates frequently and is read by many components, where selectors avoid broad re-renders. Stores with selectors (Zustand) or slices and memoized selectors (Redux Toolkit) let components subscribe to just the slice they use, avoiding the broad re-renders Context can cause.

Why must you check res.ok when fetching from an API like GitHub?

  • fetch rejects on any HTTP error automatically
  • fetch only rejects on network failure, so a 404 still resolves; check res.ok and throw yourself
  • res.ok is required to parse JSON
  • It is not necessary

Answer: fetch only rejects on network failure, so a 404 still resolves; check res.ok and throw yourself. fetch resolves even on a 404. Without checking res.ok and throwing, your error branch never runs and a 404 is treated as success.

For the recent-searches list, what is the correct order to dedupe and cap entries?

  • Cap to 5 first, then filter duplicates
  • Append to the end and never cap
  • Sort alphabetically then slice
  • Filter out the existing name, prepend the new one, then cap the length

Answer: Filter out the existing name, prepend the new one, then cap the length. Remove any earlier duplicate, put the new name at the front (most recent first), then slice to the cap. Order matters.

In the GitHub search, why keep the search result local to App but the recent list in Context?

  • Context cannot hold arrays
  • Only App needs the user object, while both the list and other components may read the recent history
  • Local state is always faster
  • Results must never be shared

Answer: Only App needs the user object, while both the list and other components may read the recent history. Keep data local to the component that owns it; share via Context only when multiple components need it, like the recent-searches history.

What is a clean rule of thumb for choosing where state should live?

  • Always put everything in global state
  • Always use a reducer
  • Keep it local unless two or more components need it; then lift it or use Context
  • Store everything in localStorage

Answer: Keep it local unless two or more components need it; then lift it or use Context. Start local; lift to a common parent or Context only when several components need the same value, and reach for a store when it updates often across many readers.