react - Hooks为什么不能写到判断里面 📅 2026/8/13 21:57:02 文章目录核心原因React 依靠“调用顺序”来匹配 Hook底层机制单向链表Linked List1. 首次挂载Mount2. 重新渲染Update如果把 Hook 写进 if 会发生什么场景演示状态错位灾难第一次渲染假设 isVip true第二次渲染假设用户切换状态isVip false正确的替代方案❌ 错误做法条件包裹 Hook✅ 正确做法Hook 内部进行条件判断总结React 规定Hook 绝对不能写在条件判断if/else、循环for/while或嵌套函数中必须始终在 React 函数组件的顶层Top Level无条件调用。这并不是 React 故意设计的限制而是由React 底层链表数据结构的存储和查找机制决定的。核心原因React 依靠“调用顺序”来匹配 Hook在函数组件中我们可能会多次调用同一个 Hook例如多次调用useStatefunction MyComponent() { const [name, setName] useState(张三); // 第 1 个 Hook const [age, setAge] useState(18); // 第 2 个 Hook const [role, setRole] useState(admin); // 第 3 个 Hook // ... }注意到没有我们调用useState时并没有传任何唯一的“Key”或标识符给 React。那么当组件重新渲染Re-render时React 是怎么知道第 1 个useState应该对应张三第 2 个对应18的呢答案就是全靠每次渲染时 Hook 的严格调用顺序Call Order。底层机制单向链表Linked List在 React 底层每个组件对应的 Fiber 节点内部维护着一条Hook 单向链表Fiber.memoizedState ──▶ [ Hook 1 (name) ] ──▶ [ Hook 2 (age) ] ──▶ [ Hook 3 (role) ] ──▶ null1. 首次挂载Mount当组件第一次渲染时每执行到一个 HookReact 就会创建一个 Hook 节点并按顺序追加到链表末尾。2. 重新渲染Update当组件状态更新重新执行函数体时React 不会重新创建链表而是将内部的游标workInProgressHook移动到链表头部。顺序每执行到一个 Hook游标就向后移动一位 (hook hook.next)从中取出上一次保存的状态。如果把 Hook 写进if会发生什么假设我们违反规则将 Hook 写在了条件语句中function BadComponent({ isVip }) { const [name, setName] useState(张三); // Hook 1 // ❌ 假设 isVip 动态改变 if (isVip) { const [vipLevel, setVipLevel] useState(5); // Hook 2 (条件 Hook) } const [age, setAge] useState(18); // Hook 3 }场景演示状态错位灾难第一次渲染假设isVip trueReact 依次按顺序构建链表Hook 1──▶name(张三)Hook 2──▶vipLevel(5)Hook 3──▶age(18)第二次渲染假设用户切换状态isVip false当组件重新执行时执行useState(张三)游标指向Hook 1──▶ 拿到张三。✅ 正常if (isVip)为false第 2 个 Hook 被跳过接着执行到useState(18)也就是原本的age但此时 React 游标移动到了链表的下一个节点Hook 2React 以为这个useState对应的是Hook 2于是把上一次vipLevel的值5赋给了age结果age的值变成了5后续所有的 Hook 索引全部错位引起极难排查的 Bug 甚至直接引发应用崩溃正确的替代方案如果你需要根据条件来决定是否使用某个逻辑或计算状态应该把条件判断写在 Hook 的内部/下方而不是把 Hook 写在条件判断里❌ 错误做法条件包裹 Hookif (isLoggedIn) { useEffect(() { fetchUserData(); }, []); }✅ 正确做法Hook 内部进行条件判断useEffect(() { // Hook 依然无条件执行但内部逻辑根据条件退出 if (!isLoggedIn) return; fetchUserData(); }, [isLoggedIn]);总结本质原因React 没有给 Hook 提供唯一标识符Key内部通过单向链表和调用顺序Index来对齐状态。后果如果在if/for中使用 Hook条件切换会导致 Hook 的调用次数与链表长度错位造成状态读取混乱。保障机制React 官方提供的 ESLint 插件eslint-plugin-react-hooks会在编译期自动检测并禁止这种违规写法rules-of-hooks。 感谢阅读想了解更多 我的博客网站 | 记录思考分享干货 我的个人主页 | 关于我、开源项目