DrillLab
第 70 / 105 道70 / 105 · #324

什么是 hooks,为什么要用

What are hooks in React and Why do we use them

先自己答,再往下看Answer it yourself first

一句话: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}