问题原因:在if-return null 语句下方使用了useEffect

判断条件依赖三个字段:basicInfo、packageBtnsData.available、showOrderDetailButton
其中showOrderDetailButton字段来源于/orderview/touch/change/getOrderDetailLinked接口(不在服务端请求)
问题场景:
- basicInfo不包含任何展示按钮字段 showCancelButton、 showPayButton、showGqButton、packageAvailable、showOrderDetailButton、showUrgeButton
- showOrderDetailButton为true
此时服务端渲染时,命中if中的条件,不会渲染UseEffect,在前端渲染时,showOrderDetailButton接口返回后,为命中if条件逻辑,导致useEffect渲染时React-diff的链表发生变化,报错Error: Rendered more hooks than during the previous render
官方解释
React#310 错误的完整信息通常是:
"Rendered fewer hooks than expected. This may be caused by an accidental early return statement."
官方解释是:在组件渲染过程中,React 发现实际调用的 Hook 数量少于预期。这通常是由于在条件语句或提前返回中使用了 Hook,导致 Hook 的调用顺序不一致。
底层原理
React Hooks 的实现依赖于调用顺序的稳定性,这是因为:
- Hook 链表:React 在内部维护一个 Hook 的链表,每个 Hook 都按照它们被调用的顺序存储在链表中。
- 状态对应 :每次组件渲染时,React 都依赖 Hook 的调用顺序来正确关联状态。例如,第一个
useState
调用总是返回第一个状态,第二个调用返回第二个状态,以此类推。 - 渲染一致性:React 要求在组件的每次渲染中,Hook 的调用顺序必须完全相同。这样才能保证状态的一致性。
为什么不能那样写
以下是几种常见的会导致 React#310 错误的写法及其原因:
1. 条件性使用 Hook
scss
function MyComponent({ show }) {
if (show) {
const [value, setValue] = useState(null); // 错误:条件性使用 Hook
}
// ...
}
问题 :当 show
的值变化时,Hook 的调用顺序会改变,导致 React 无法正确关联状态。
2. 提前返回中使用 Hook
javascript
function MyComponent({ data }) {
if (!data) return null;
const [value, setValue] = useState(null); // 错误:可能在渲染中被跳过
// ...
}
问题 :当 data
为 falsy 时,Hook 不会被调用,破坏了调用顺序的一致性。
3. 循环或嵌套中使用 Hook
javascript
function MyComponent({ items }) {
items.forEach(item => {
const [value, setValue] = useState(item); // 错误:在循环中使用 Hook
});
// ...
}
问题:循环次数可能变化,导致 Hook 调用数量不一致。
正确写法
1. 无条件使用 Hook
scss
function MyComponent({ show }) {
const [value, setValue] = useState(null); // 正确:总是在相同位置调用
if (show) {
// 使用 value
}
// ...
}
2. 提前返回前确保 Hook 调用
javascript
function MyComponent({ data }) {
const [value, setValue] = useState(null); // 正确:在所有路径前调用
if (!data) return null;
// ...
}
3. 使用数组管理多个状态
scss
function MyComponent({ items }) {
const [values, setValues] = useState(items); // 正确:用数组管理多个值
// ...
}
总结
React#310 错误的本质是破坏了 React Hooks 的核心规则:不要在循环、条件或嵌套函数中调用 Hook。React 依赖 Hook 调用的顺序来正确关联状态,任何可能导致调用顺序变化的写法都会导致错误。理解这一原理有助于编写更健壮的 React 组件。