DrillLab
八股题库Question bank

105 道问答题,一道一卡105 questions, one card each

默认只显示问题 —— 先自己在心里答一遍,再展开对答案。答不上来就标「不会」,下次抽认卡会先抽它。Only the question shows by default. Answer it in your head first, then open the answer. Mark the ones you miss; the flashcard round puts those first.

只看这一节One lesson onlyHooks 四问4 道4 questions看全部 105 道 →All 105 questions →
0 / 105道自评过self-assessed
0Got it0模糊Shaky0不会No idea105还没做Not seen
正在读这台浏览器里的标记…Reading your marks from this browser…
标记不是打分,是给下一轮排队Marks are a queue, not a score

标「不会」的题会排到抽认卡最前面,标「会」的进低频池排最后。所以别客气 —— 觉得答得磕磕巴巴就标「模糊」。准备好了就去抽认卡“No idea” jumps to the front of the next round; “got it” drops to the back. So be honest — if it came out shaky, mark it shaky. Then go run a flashcard round.

找一道题Find one
按方向、掌握状态筛Filter by topic and mark

题目Questions

筛出 4 道。4 of 4 questions.
React 与生态React & ecosystem#324

什么是 hooks,为什么要用

What are hooks in React and Why do we use them

看答案Show answer

一句话:hooks 是一组use 开头的函数, 让函数组件也能有状态和副作用

解决三个真实痛点(这三条是标准答案):

  • 逻辑复用难。以前只有 HOC 和 render props, 两者都会造成 wrapper 嵌套(见 #335)。 自定义 hook 是平的。
  • 逻辑被生命周期切碎。一个「订阅 + 取消订阅」的完整逻辑 被迫拆到两个生命周期方法里;useEffect 让它们写在一起。
  • this 太容易出错。函数组件没有 this

常用的:useStateuseEffectuseContextuseRefuseMemouseCallbackuseReducer; React 18 加了useIduseTransitionuseDeferredValueuseSyncExternalStore

两条规则(必答):

  1. 只在最顶层调用—— 不能放在 if、循环、 嵌套函数里。
  2. 只在函数组件或自定义 hook 里调用。

为什么有第一条 —— 这是追问点。React 不知道你的 hook 叫什么名字, 它是按调用顺序把每个 hook 的状态 存在一条链表上的。 如果 hook 写在 if 里, 某次渲染少调了一个, 后面所有 hook 的下标就全错位了——useState 会拿到别人的值。
能答出「靠调用顺序而不是名字」就说明真理解了。

In one line: hooks are functions that start with use and give function components state and side effects.

They fix three real pain points — these three are the standard answer:

  • Reusing logic was hard. All you had were HOCs and render props, and both leave you with wrapper nesting (see #335). A custom hook is flat.
  • Logic got chopped up by the lifecycle. One coherent “subscribe and unsubscribe” had to be split across two lifecycle methods; useEffect keeps them side by side.
  • this was too easy to get wrong. Function components have no this.

The common ones: useState, useEffect, useContext, useRef, useMemo, useCallback, useReducer; React 18 added useId, useTransition, useDeferredValue and useSyncExternalStore.

Two rules — always say them:

  1. Call them at the top level only — never inside an if, a loop, or a nested function.
  2. Call them only from a function component or a custom hook.

Why the first rule exists — that is the follow-up. React does not know what your hook is called. It keeps each hook’s state in a linked list, indexed by call order. Put a hook inside an if, skip it on one render, and every index after it shifts useState hands you somebody else’s value.
Say “by call order, not by name” and they know you really understand it.

JSX为什么不能写在条件里示意Illustrative
1// ✗ 顺序会变,后面所有 hook 错位
2function Bad({ show }) {
3 if (show) {
4 const [a] = useState(1); // 有时调有时不调
5 }
6 const [b] = useState(2); // b 可能拿到 a 的槽位
7}
8
9// ✓ hook 在顶层,条件放里面
10function Good({ show }) {
11 const [a] = useState(1);
12 const [b] = useState(2);
13 useEffect(() => {
14 if (!show) return; // 条件判断放 effect 内部
15 // ...
16 }, [show]);
17}
1// ✗ the order changes, and every hook after it shifts
2function Bad({ show }) {
3 if (show) {
4 const [a] = useState(1); // called sometimes, skipped other times
5 }
6 const [b] = useState(2); // b may end up in a's slot
7}
8
9// ✓ hooks at the top level, the condition inside
10function Good({ show }) {
11 const [a] = useState(1);
12 const [b] = useState(2);
13 useEffect(() => {
14 if (!show) return; // put the condition inside the effect
15 // ...
16 }, [show]);
17}
React 与生态React & ecosystem#339

useMemo vs useCallback

useMemo vs useCallback

看答案Show answer

一句话:useMemo 缓存 「函数的返回值」,useCallback 缓存 「函数本身」。两者都靠依赖数组决定要不要重算。

实际上 useCallback(fn, deps)完全等价于useMemo(() => fn, deps)——后者是前者的语法糖。 这句能答出来会加分。

缓存什么什么时候用
useMemo计算结果(值 / 对象 / 数组)① 计算真的贵(大列表排序过滤) ② 结果要当 props 传给 memo 组件 ③ 结果要当别的 hook 的依赖
useCallback函数引用① 函数要传给 memo 组件 ② 函数是 useEffect 的依赖 ③ 自定义 hook 对外暴露的函数

什么时候不该用(这半边很多人答不出):两者本身都有成本—— 要存旧值、要比较依赖。 给一个 a + buseMemo 是纯亏。「先测量,再优化」; 默认不加,profiler 显示有问题再加。

最常见的误用:包了 useCallback但依赖数组里放了每次都变的东西 —— 等于没包,还多付了比较成本。

会追问:「React 19 的编译器会怎样?」—— React Compiler 能自动插入记忆化,大部分手写的useMemo /useCallback 将不再必要。 知道这个趋势会显得你在跟进。

In one line: useMemo caches “what a function returns”, useCallback caches “the function itself”. Both use the dependency array to decide whether to recompute.

In fact useCallback(fn, deps) is exactly the same thing as useMemo(() => fn, deps) the former is sugar for the latter. Saying this scores points.

What it cachesWhen to use it
useMemoA computed result (value / object / array)① the work is genuinely expensive (sorting or filtering a big list) ② the result goes to a memo component as a prop ③ the result is a dependency of another hook
useCallbackA function reference① the function goes to a memo component ② the function is a dependency of useEffect③ the function is part of a custom hook’s public API

When not to use them — most people miss this half: both cost something — the old value is kept and the dependencies get compared. Wrapping a + b in useMemo is a pure loss. “Measure first, then optimise”: leave them out by default and add them when the profiler says so.

The most common misuse: wrapping something in useCallback but putting a value that changes every render in the dependency array — the cache never hits, and you paid for the comparison on top.

Follow-up: “What changes with the React 19 compiler?” — React Compiler can insert memoisation for you, so most hand-written useMemo and useCallback will stop being necessary. Knowing the direction shows you are keeping up.

JSX两者的区别与等价关系示意Illustrative
1// useMemo:缓存值
2const sorted = useMemo(() => items.sort(cmp), [items]);
3
4// useCallback:缓存函数
5const onPick = useCallback((id) => setPicked(id), []);
6
7// 两者的关系
8useCallback(fn, deps) === useMemo(() => fn, deps)
9
10// ✗ 常见误用:依赖每次都变,等于没缓存
11const onSave = useCallback(() => save(config), [{ ...config }]);
12// ↑ 每次都是新对象
1// useMemo caches a value
2const sorted = useMemo(() => items.sort(cmp), [items]);
3
4// useCallback caches a function
5const onPick = useCallback((id) => setPicked(id), []);
6
7// How the two relate
8useCallback(fn, deps) === useMemo(() => fn, deps)
9
10// ✗ a common mistake: the dependency changes every time, so nothing is cached
11const onSave = useCallback(() => save(config), [{ ...config }]);
12// ↑ a new object every render
React 与生态React & ecosystem#346

React.memo vs useMemo

React.memo vs useMemo

看答案Show answer

一句话:React.memo 是 「组件」级的 —— 决定要不要重新渲染整个组件useMemo 是 「值」级的 —— 决定要不要重新计算一个值

React.memouseMemo
是什么高阶组件hook
用在哪包在组件外面写在组件里面
比较什么props(浅比较)依赖数组
省掉什么一次组件渲染一次计算

关键:三个必须配套用。只在子组件上加 React.memo通常一点效果都没有—— 因为父组件每次渲染都会给出新的对象和函数字面量, 浅比较必然判定「变了」。必须同时用useMemo 稳住对象、useCallback 稳住函数。

会追问:memo 能拦住 context 变化吗?」——拦不住memo 只比 props, context 走另一条通道。所以 context value 必须useMemo—— 这正是我们那道主题切换变式题的核心考点, 删掉 useMemo 后实测「功能测试全绿、 只有引用稳定性那条红」。
children 会破坏 memo 吗?」—— 会,children 也是 prop, 而 JSX 每次都产生新元素对象。

In one line: React.memo works at the “component” level — it decides whether to re-render a whole component; useMemo works at the “value” level — it decides whether to recompute one value.

React.memouseMemo
What it isA higher-order componentA hook
Where it goesWrapped around the componentInside the component
What it comparesprops (shallow)the dependency array
What it savesone component renderone computation

The key point: all three go together. Dropping React.memo on a child usually does nothing at all — the parent hands down fresh object and function literals on every render, so the shallow compare is bound to say “changed”. You must also stabilise objects with useMemo and functions with useCallback.

Follow-up: “Can memo stop a context change?” — no it cannot. memo only compares props; context travels a separate channel. That is why a context value has to be wrapped in useMemo — exactly the point of our theme switching variant, where deleting the useMemo left every behaviour test green and only the reference-stability test red.
“Does children break memo?” — yes. children is a prop too, and JSX produces a new element object every time.

React 与生态React & ecosystem#340

自定义 hook 是干什么的,命名有什么约定

What are custom hooks for and what is the naming convention for them

看答案Show answer

一句话:「带状态的逻辑」抽出来复用。 命名必须以 use 开头

为什么必须 use 开头—— 这是考点,不是风格问题:

  • ESLint 靠这个前缀识别它是 hook, 才能检查 hooks 规则 (react-hooks/rules-of-hooks)。 不加前缀,你在里面违规调用 hook 也不会有人警告你。
  • 它同时也是给读代码的人的信号:这个函数里可能有状态, 所以它有调用位置的限制

关键概念:复用的是逻辑,不是状态。两个组件各自调 useCounter(), 得到的是两份完全独立的状态。 想共享状态得用 Context 或状态库。这一条是高频追问,很多人答错。

什么时候该抽:同一组useState +useEffect 的组合在两处以上出现; 或者一个组件里的 effect 逻辑长到 让主体读不懂了。

会追问:「自定义 hook 能返回什么?」—— 随意。约定是「像 useState 一样返数组」 (调用方好重命名)、 「三个以上返对象」(不用记顺序)。
「里面能调别的 hook 吗?」—— 能,这正是它的意义;但同样要遵守两条规则。

In one line: pull “logic that carries state” out so it can be reused. The name must start with use.

Why the use prefix is mandatory — this is the point being tested, and it is not about style:

  • ESLint uses the prefix to recognise it as a hook so it can enforce the rules of hooks (react-hooks/rules-of-hooks). Without the prefix, you can break those rules inside it and nobody warns you.
  • It is also a signal to whoever reads the code: this function may hold state, so there are limits on where you may call it.

The key idea: you reuse the logic, not the state. Two components that each call useCounter() get two completely independent pieces of state. To share state you need Context or a state library. This is a frequent follow-up and a lot of people get it wrong.

When to extract one: the same combination of useState and useEffect shows up in two or more places; or the effect logic in one component has grown long enough that you can no longer read the component itself.

Follow-up: “What can a custom hook return?” — anything. The convention is “return an array like useState does” (so the caller can rename freely) and “return an object once there are three or more values” (so nobody has to remember the order).
“Can it call other hooks?” — yes, that is the whole point; the same two rules still apply.

JSX自定义 hook 的形状示意Illustrative
1// 一个真实好用的:把「值 + 存 localStorage」打包
2function useLocalStorage(key, initial) {
3 const [value, setValue] = useState(() => {
4 try {
5 const raw = localStorage.getItem(key);
6 return raw ? JSON.parse(raw) : initial;
7 } catch {
8 return initial; // 隐私模式读不了就用默认值
9 }
10 });
11
12 useEffect(() => {
13 try { localStorage.setItem(key, JSON.stringify(value)); } catch {}
14 }, [key, value]);
15
16 return [value, setValue]; // 像 useState 一样返数组
17}
18
19// 用起来
20const [theme, setTheme] = useLocalStorage("theme", "light");
21
22// 注意:两个组件各调一次,得到的是两份独立状态,不是共享的
1// One that is genuinely useful: a value together with storing it in localStorage
2function useLocalStorage(key, initial) {
3 const [value, setValue] = useState(() => {
4 try {
5 const raw = localStorage.getItem(key);
6 return raw ? JSON.parse(raw) : initial;
7 } catch {
8 return initial; // private mode cannot read, so use the default
9 }
10 });
11
12 useEffect(() => {
13 try { localStorage.setItem(key, JSON.stringify(value)); } catch {}
14 }, [key, value]);
15
16 return [value, setValue]; // returns an array, the same shape as useState
17}
18
19// Using it
20const [theme, setTheme] = useLocalStorage("theme", "light");
21
22// Note: two components each calling it get two separate states, not a shared one

这些题从哪来Where these come from

99 道来自面试题库 #269–#387;TypeScript 深度那 6 道是 DrillLab 自出的(senior 补强,卡片上有标注)。答案都是 DrillLab 写的,所以讲解里的代码块一律标「示意」。每道题都能点回它出处的那一节课。99 questions come from the interview bank (#269–#387); the 6 TypeScript deep-dive ones are DrillLab-made (marked on the card). All answers are written by DrillLab, so every code block here is labelled “demo”. Each card links back to the lesson it came from.