๐Ÿณ IntuitiveFE
Login
โ† All concepts

React Hooks In Depth

โฑ๏ธ ~5-minute bite ยท solve the sandbox to master

0%lesson
๐Ÿง’

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.

js
1// โœ— Breaks the linked list โ€” conditional hook
2if (isLoggedIn) {
3 const user = useContext(UserCtx); // only sometimes hook #2
4}
5ย 
6// โœ“ Always same number of hooks in same order
7const user = useContext(UserCtx);
8if (!isLoggedIn) return null;
๐ŸŽ›๏ธ

Interactive Sandbox

โ€” Move something, see it react instantly.

Hook

useState โ€” Type signature
js
1useState<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.

Gotcha: The setter function is stable (same reference across renders) โ€” safe to include in useEffect deps without causing re-runs.
Insight: React batches setState calls inside event handlers in React 18. Two setStates in one click = one re-render. This is automatic โ€” no need for unstable_batchedUpdates.
Explored 1/10:๐Ÿ”ข๐Ÿ”„๐Ÿ“ก๐Ÿ“Œ๐Ÿงฎ๐Ÿ”—๐Ÿ”Œ๐Ÿ“โณ๐Ÿšฆ
๐ŸŽฏ

Challenge

Explore all 10 hooks. Read each signature, example, and common mistake.

Try it
๐ŸŽฏ

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?

js
1// Without cleanup โ€” stale subscription, memory leak
2useEffect(() => {
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.
9useEffect(() => {
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

js
1// useId โ€” stable, hydration-safe ID (not for list keys)
2const 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
8const 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.

ts
1function 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.
1/4

What is a stale closure bug in useEffect?