React Hooks In Depth
โฑ๏ธ ~5-minute bite ยท solve the sandbox to master
5-Year-Old Metaphor
โ The physical, real-world picture. No jargon.๐ Hooks are like electrical outlets โ each is a standard interface connecting your component to a specific React capability. Wrong outlet for the job = bugs.
๐ข State hooks
โข useState โ simple values
โข useReducer โ transitions
๐ Context hooks
โข useContext โ subscribe to shared values
๐ Ref hooks
โข useRef โ mutable, no re-render
โข useImperativeHandle โ expose ref API
๐ Effect hooks
โข useEffect โ sync after paint
โข useLayoutEffect โ sync before paint
๐ฆ Performance hooks
โข useMemo โ memoize values
โข useCallback โ stable functions
โข useTransition โ non-urgent updates
โข useDeferredValue โ defer reading
The Rules of Hooks โ and why they exist
React stores hook state in a linked list on the fiber node. The list is ordered โ hook #1, hook #2, hook #3. If a conditional hides hook #2 on one render, hook #3 shifts to position #2 on the next. React reads the wrong state. This is why hooks cannot be called inside conditions, loops, or nested functions.
| 1 | // โ Breaks the linked list โ conditional hook |
| 2 | if (isLoggedIn) { |
| 3 | const user = useContext(UserCtx); // only sometimes hook #2 |
| 4 | } |
| 5 | ย |
| 6 | // โ Always same number of hooks in same order |
| 7 | const user = useContext(UserCtx); |
| 8 | if (!isLoggedIn) return null; |
Interactive Sandbox
โ Move something, see it react instantly.Hook
| 1 | useState<T>(initialValue: T | (() => T)): [T, Dispatch<SetStateAction<T>>] |
When to use
Simple, independent state values. Toggles, counters, input values, form fields. Each useState manages one piece of state.
vs Alternative
vs useReducer: use useState for simple independent values. When state transitions have complex logic, multiple related fields, or need to be tested independently โ reach for useReducer.
Challenge
Explore all 10 hooks. Read each signature, example, and common mistake.
Why Should I Care?
โ The exact interview question + the bug it kills.Interview questions
Q: What are the Rules of Hooks and why do they exist?
Only call hooks at the top level (never inside conditions, loops, or nested functions). Only call hooks from React function components or custom hooks. The reason: React tracks hook state by call order in a linked list โ if the order changes between renders, state gets mismatched.
Q: When do you use useReducer instead of useState?
When state transitions have complex logic (multiple fields changing together), when next state depends on the current in non-trivial ways, or when you want the transitions to be testable independently. useReducer also makes it easy to share the dispatch function down through context.
Q: Why is useEffect cleanup critical?
| 1 | // Without cleanup โ stale subscription, memory leak |
| 2 | useEffect(() => { |
| 3 | const sub = stream.subscribe(setData); |
| 4 | // forgotten: return () => sub.unsubscribe(); |
| 5 | }, [stream]); |
| 6 | ย |
| 7 | // With cleanup โ React calls cleanup before re-running |
| 8 | // and on unmount. No leaks. |
| 9 | useEffect(() => { |
| 10 | const sub = stream.subscribe(setData); |
| 11 | return () => sub.unsubscribe(); |
| 12 | }, [stream]); |
The Deep Dive
โ Spec refs, engine internals, the minutiae.React hooks implementation โ linked list on fiber
Each function component has a fiber node in the React tree. The fiber stores a linked list of "hooks" โ each entry holds the hook's current state, the queue of pending updates, and a pointer to the next hook. React walks this list on every render, matching hooks by position (not by name). This is why hooks cannot be conditional โ the list must have the same length and order every render.
React 18 new hooks
| 1 | // useId โ stable, hydration-safe ID (not for list keys) |
| 2 | const id = useId(); // e.g. ":r3:" |
| 3 | <label htmlFor={id}>Email</label> |
| 4 | <input id={id} /> |
| 5 | ย |
| 6 | // useSyncExternalStore โ subscribe to external stores |
| 7 | // Used by Redux, Zustand, etc. under the hood |
| 8 | const snapshot = useSyncExternalStore( |
| 9 | store.subscribe, // subscribe fn |
| 10 | store.getSnapshot, // get current value (client) |
| 11 | store.getSnapshot, // get current value (server) |
| 12 | ); |
Custom hooks โ composition primitives
Custom hooks are the real power of the hooks API. Any function that starts with "use" and calls React hooks is a custom hook. They let you extract and reuse stateful logic without HOC wrapper hell. Examples: useDebounce, useFetch, useLocalStorage, useWindowSize. The rule: if you find yourself copying useState + useEffect logic between components, extract a custom hook.
| 1 | function useDebounce<T>(value: T, delay: number): T { |
| 2 | const [debounced, setDebounced] = useState(value); |
| 3 | useEffect(() => { |
| 4 | const id = setTimeout(() => setDebounced(value), delay); |
| 5 | return () => clearTimeout(id); |
| 6 | }, [value, delay]); |
| 7 | return debounced; |
| 8 | } |
React 19 new hooks (upcoming)
React 19 ships useFormStatus (read the nearest form's pending/data state inside a submit button), useOptimistic (show an optimistic update while a Server Action is in flight), and use(promise) (read a promise or context inside render โ works with Suspense). These hooks are built for the RSC + Server Actions model.
Interview Questions
โ Real questions from real interviews โ with answers.Always call hooks at the top level, only in React functions โ because hook state is stored in a fixed-order linked list per fiber.
Lazy initializer `useState(() => exp())` runs once; `useState(exp())` runs on every render.
Use useReducer when state transitions are complex, interrelated, or need to be unit-tested independently.
Refs mutate silently without triggering re-renders โ perfect for bookkeeping values that don't affect the UI.
A closure captures a variable at creation time; if deps are missing, the effect always sees the stale value.
A ref holds the latest version of a callback; a stable wrapper function reads from the ref โ stable reference, always-fresh values.
Memory Game
โ Quick quiz โ lock the concept in long-term memory.What is a stale closure bug in useEffect?