Skip to main content

4. Context, Stores & Custom Hooks

Goal for this page: stop passing the same props through many layers ("prop drilling"), move shared state into a Store, keep logic in a controller, and start reusing logic with custom hooks. This is the page where DevDirectory starts to really look like an AquaGen feature.


Step 4.1 — A store with useContext

What & why: Some state is needed all over the app — a theme, the logged-in user, a list of favorites. Context lets any component read that state without threading it through every parent. In AquaGen this lives in a Store file: a Context provider that holds state with useState and exposes it (that's exactly what AppStore and each feature store are).

Learn:

Your task: Add a Favorites feature. Create src/store/FavoritesStore.jsx: a Context provider that holds a list of favorited item IDs in useState and exposes add, remove, and the current list. Put a ⭐ button on each Card and add a "Favorites" page that reads from the store. Confirm favoriting on the list page shows up on the Favorites page without passing props between them.


Step 4.2 — Move logic into a controller

What & why: A store should hold state; the logic that decides what goes into it belongs in a controller. In AquaGen, controller/ sits between dataSource/ (raw API) and the store/ + components/ — it fetches, shapes, and combines data so components stay simple.

Learn:

Your task: Create src/controller/itemsController.js. Move any data-shaping logic out of your components into it — e.g. a function that calls getItems() from your dataSource, sorts or filters the result, and returns exactly what the list page needs. Your component should now just call the controller and render.

Optional: useReducer

When a single store manages many related fields, some teams reach for useReducer instead of several useState calls. It's good to know — but AquaGen's stores use Context + useState, so that's the pattern to practice here. Treat useReducer as background reading, not a requirement.


Step 4.3 — Extract logic into custom hooks

What & why: When two components need the same logic (e.g. "fetch this URL, track loading/error"), copy-pasting it violates DRY. A custom hook is just a function starting with use that packages reusable stateful logic. AquaGen keeps shared hooks in libs/shared/src/hooks.

Learn:

Your task:

  1. Extract your fetch + loading + error pattern into a single custom hook, e.g. useFetch(url) (put it in src/hooks/). Use it on both the list and detail pages.
  2. Write a second hook useDebounce(value, delay) and use it to delay firing the search request while the user is still typing.

Checkpoint

DevDirectory now has a store/ (Context), a controller/, a dataSource/, reusable hooks/, and components/ — the same layers as a real AquaGen feature. One page left: make it clean and production-shaped.

Next:Best practices & workflow