React报错#310复盘小结
- 问题背景
 - 解决方案
 - 原理&学习
 - 引发错误情况
 - 1. 不要在循环,条件或嵌套函数中调用 Hook
 - 2. 把所有的钩子移到组件的顶层,在任何可能返回值的条件之上。
 
- 总结
 
问题背景
apm报错:Minified React error #310
 https://reactjs.org/docs/error-decoder.html/?invariant=310
 ![[图片]](https://img-blog.csdnimg.cn/cd24ac37d8c54d7d98ba883ea5f803cf.png)
当我们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会产生"Rendered more hooks than during the previous render"错误。
解决方案
const App = () => {
    …………
+   const [loading, setLoading] = useState(false)
+   useEffect(函数1, [……])
    
    if (条件) {
      try {
        …………
      } catch {}
    }
   
-   const [loading, setLoading] = useState(false)
-   useEffect(函数1, [……])
    return (……)
}
 
if中 catch {} 阻塞了后续hook的渲染,为了解决该错误,将所有的钩子移到函数组件的顶层,以及不要在条件中使用钩子。
原理&学习
React 靠的是 Hook 调用的顺序。
 在正常的程序中,Hook 的调用顺序在每次渲染中都是相同的
const App = () => {
    const [loading, setLoading] = useState(false)
    useEffect(函数1)
    const [test, setTest] = useState('name')
}
 
// 首次渲染
useState('false')            // 1. 使用 false 初始化变量名为 loading 的 state
useEffect(函数1)             // 2. 添加 effect 以保存 form 操作
useState('name')            // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
// 二次渲染
useState('false')            // 1. 读取变量名为 loading 的 state(参数被忽略)
useEffect(函数1)             // 2. 替换保存 form 的 effect
useState('name')            // 3. 读取变量名为 surname 的 state(参数被忽略)
 
只要 Hook 的调用顺序在多次渲染之间保持一致,React 就能正确地将内部 state 和对应的 Hook 进行关联。
const App = () => {
    const [loading, setLoading] = useState(false)
    useEffect(函数1)
    if (条件) {
      try {
        …………
      } catch {}
    }
    const [test, setTest] = useState('name')
}
 
If条件的存在可能导致触发catch,导致两种渲染情况不一致:
// 没进入到catch
useState('false')            // 1. 使用 false 初始化变量名为 loading 的 state
useEffect(函数1)             // 2. 添加 effect 以保存 form 操作
useState('name')            // 3. 使用 'Poppins' 初始化变量名为 surname 的 state
// 进入到catch中
useState('false')             // 1. 读取变量名为 loading 的 state(参数被忽略)
useEffect(函数1)              // 2. 替换保存 form 的 effect
//useState('name')           // 3. 此 Hook 被忽略!
 
引发错误情况
https://zh-hans.reactjs.org/docs/hooks-rules.html
1. 不要在循环,条件或嵌套函数中调用 Hook
export default function App() {
  const [counter, setCounter] = useState(0);
-  if (counter > 0) {
-    useEffect(() => {
-      console.log(counter);
-    });
-  }
// 将if条件语句移到useEffect钩子内部
+  useEffect(() => {
+    if (counter > 0) {
+      console.log(counter);
+    }
+  });
  return (
    ……
}
 
2. 把所有的钩子移到组件的顶层,在任何可能返回值的条件之上。
export default function App() {
  const [counter, setCounter] = useState(0);
+ const [color, setColor] = useState('salmon');
  if (counter > 0) {
    return <h2>Returning early</h2>;
  }
  // Error: 该hook在counter<=0条件时,才被调用
- const [color, setColor] = useState('salmon');
  
  return (
    <div><button onClick={() => setCounter(counter + 1)}>toggle loading</button><h1>Hello world</h1></div>
  );
}
 
总结
- 只从React函数组件或自定义钩子中调用Hook
 - 只在最顶层使用 Hook
 - 不要在循环,条件或嵌套函数中调用 Hook
 - 确保总是在你的 React 函数的最顶层以及任何 return 之前使用 Hook
 
这有助于React在多个useState和useEffect调用之间保留钩子的状态。



















