React Hooks 限制:为什么不能在 if 语句中使用 Hook?

599 阅读3分钟

React Hooks 限制:为什么不能在 if 语句中使用 Hook?

React Hooks 是 React 16.8 引入的一种全新 API,让我们可以在函数组件中使用 state、生命周期和其他 React 特性。虽然使用方便,但 Hooks 也有一些必须遵守的”规则”,其中最容易踩坑的一条是:不能在 if 语句中使用 Hook。

一、Hooks 到底做了什么?

我们在组件中常这样写:

function MyComponent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState('');
}

React 是如何追踪 count 和 name 的状态呢?答案是:React 依赖 Hook 的调用顺序,而不是变量名。

每次组件渲染,React 都会按照 Hook 的执行顺序,在内部记录每一个 Hook 对应的状态,就像维护一个数组一样:

hookStates = [
  useState(0),   // index 0
  useState('')   // index 1
];

这意味着 只要调用顺序不变,React 就能正确管理状态

二、if 中使用 Hook 会导致什么问题?

错误示例:

function MyComponent({ isLoggedIn }) {
  if (isLoggedIn) {
    useState(1); // ❌ 错误用法
  }
  useEffect(() => {
    // ...
  }, []);
}

第一次渲染时,isLoggedIn 是 true,useState(1) 被执行,React 记录它为第一个 Hook;

第二次渲染时,如果 isLoggedIn 是 false,useState(1) 没执行,useEffect 成了第一个 Hook。

这时,React 会抛出这样的错误:

Rendered fewer hooks than expected. This may be caused by an early return statement.

Hook 顺序对不上了,状态管理就会完全错乱。

三、React 官方的 Hook 规则

React 提供了两条核心规则来避免这类问题:

  • 只能在最顶层调用 Hook:不要在条件、循环、嵌套函数中调用 Hook;
  • 只能在 React 函数组件或自定义 Hook 中调用 Hook

这些规则的目标就是保证 Hook 的调用顺序在每次渲染中始终一致。

可以借助官方的 eslint-plugin-react-hooks 插件来强制执行这些规则。

npm install eslint-plugin-react-hooks --save-dev

.eslintrc.js 示例配置:

module.exports = {
  plugins: ['react-hooks'],
  rules: {
    'react-hooks/rules-of-hooks': 'error',
    'react-hooks/exhaustive-deps': 'warn',
  },
};

四、如何正确写法?

❌ 错误示例

if (isLoggedIn) {
  const [user, setUser] = useState(null); // ❌ 不允许
}

✅ 正确写法

const [user, setUser] = useState(null);

useEffect(() => {
  if (isLoggedIn) {
    // 安全地触发副作用
    setUser({ name: '张三' });
  }
}, [isLoggedIn]);

即:Hook 在组件顶层调用,条件逻辑放在 useEffect 或其他函数中处理。

五、为什么必须这么严格?

React 使用的是链式调用顺序识别状态,不像 Vue 有响应式 Proxy。

如果你允许在 if、for 中灵活使用 Hook,那状态的索引和顺序在每次 render 中可能就会不一致,导致内存错位、Bug 难以排查。

换句话说:这并不是语法限制,而是 React 的底层实现机制要求必须严格按照顺序执行。

六、自定义 Hook 中也不能滥用条件

function useCustomHook() {
  if (someCondition) {
    useState(...); // ❌ 同样不允许
  }
}

即使你在自定义 Hook 中,也必须保证 Hook 调用顺序一致

七、总结

规则原因
Hooks 必须在函数组件顶层调用保证顺序一致,状态稳定
不能在 if、for、嵌套函数中使用避免顺序错乱,引发错误

正确的姿势是:

  • 把所有 Hook 写在函数最顶层;
  • 把条件逻辑放在 useEffect、回调或事件处理函数中处理。

最后

虽然 Hook 限制看似严格,但理解背后的设计哲学,就会发现这是一种为了 “安全和性能” 而做出的权衡。

记住:Hook 不是什么魔法,它是有顺序的栈结构。顺序一乱,Bug 就来了。

祝你写出干净稳定的 React 组件!