1. Core Concepts
Custom Hooks: JavaScript functions that start with use... and call other hooks. They allow you to share logic (like fetching or form handling) between components without sharing UI.
useReducer: An alternative to useState for complex logic. You dispatch "actions" (instructions) to a "reducer" (function) that calculates the new state.
Memoization (useMemo/useCallback): Performance tools. They tell React: "If these inputs haven't changed, don't re-calculate this value or re-create this function."
1.1 Deep Dive: The Rules of Hooks
Hooks are magic, but they have rules. They rely on call order.
1. Only call hooks at the top level. Never inside loops, conditions, or nested functions.
2. Only call hooks from React functions.
1.2 Industry Standard: The "use" Prefix
If a function uses a hook, it must be named useSomething. This is not just a convention; linters rely on it to enforce the Rules of Hooks. If you write a helper function that calls useContext but name it getAuth(), you will break the linter and potentially your app.
📚 Deep Dive (Documentation)
Refactor to Custom Hooks
As a developer, I want to reuse logic across components so that I don't violate DRY (Don't Repeat Yourself) principles.
- useFetch(url):
- Accepts a URL string.
- Returns
{ data, loading, error }. - Handles
AbortControllerto cancel requests on unmount.
- useLocalStorage(key, initialValue):
- Syncs state with
localStorage. - Updates storage when state changes.
- Initializes from storage if available.
- Syncs state with
- Generics: Make
useFetch<T>anduseLocalStorage<T>type-safe. - useWindowSize: Create a hook that tracks window dimensions. MUST remove the event listener on unmount.
- useDebounce: Create a hook to delay API calls in the search bar (500ms delay).
User Story: As a developer, I need to manage a complex shopping cart state where multiple actions (add, remove, update quantity, clear) affect the same data.
Acceptance Criteria:
- Create a `cartReducer` function.
- Handle actions: `ADD_ITEM`, `REMOVE_ITEM`, `UPDATE_QTY`, `CLEAR_CART`.
- Use `useReducer` in the `CartContext`.
- Ensure state updates are immutable.
3. The Bug Hunt
🐛 Scenario: The Expensive Calculation
The Setup:
function Dashboard({ data }) {
// This runs on EVERY render, even if 'data' hasn't changed!
const processedData = expensiveMathOperation(data);
return <Chart data={processedData} />;
}
The Bug: The dashboard is laggy because expensiveMathOperation takes 200ms to run, and the component re-renders whenever the parent updates.
The Task:
- Wrap the calculation in
useMemo. - Explain why
useMemoneeds a dependency array.