跳到主要内容

为何Hooks要依赖于调用顺序

面试速答

  • Hooks 依赖调用顺序,不是“官方硬规定”,而是 实现模型决定的
  • React 在执行函数组件时,需要按顺序把每个 Hook 调用映射到内部状态链表或槽位。
  • 一旦顺序变化,后面的状态、effect、ref 全都会对错位置。

用极简模型理解

假设组件里有两个 Hook:

function Demo() {
const [count] = useState(0)
const [name] = useState('Tom')
}

React 可以把它粗略理解成:

hookStates[0] -> count
hookStates[1] -> name

下一次 render 时,只要还是这个顺序,React 就能正确取回它们。

一旦条件分支介入会怎样

function Demo({visible}: {visible: boolean}) {
const [count] = useState(0)

if (visible) {
useEffect(() => {}, [])
}

const [name] = useState('Tom')
}

visibletrue 变成 false 时,第二个 Hook 的位置就变了:

  • 原来第 2 个位置是 useEffect
  • 现在第 2 个位置变成 useState(name)

后续整个映射都会错位。

为什么 React 不用名字定位

因为 Hook 是普通函数调用,不像 class 的 this.state.xxx 那样天然有名字锚点。

React 选择“按调用顺序记录”的方案,换来的是:

  • API 足够简洁
  • 自定义 Hook 易于组合
  • 内部实现统一

代价就是调用顺序必须稳定。

标准答法

问:为什么 Hooks 必须依赖调用顺序?

答:因为 React 在函数组件执行时,是按顺序记录和读取每个 Hook 的内部状态节点的。只有顺序稳定,React 才知道当前这次 useState 对应上次的哪一个状态。

易错点

  • 只会背“不能放条件里”,解释不出为什么。
  • 以为 React 能通过变量名判断 Hook 身份。
  • 不理解自定义 Hook 也是靠调用顺序展开的。

速记要点

  • Hook 没有名字索引,靠顺序定位。
  • 条件、循环会破坏顺序。
  • 这就是 Hooks 规则的底层原因。