React报错#310复盘小结

React报错#310复盘小结

    • 问题背景
    • 解决方案
    • 原理&学习
    • 引发错误情况
      • 1. 不要在循环,条件或嵌套函数中调用 Hook
      • 2. 把所有的钩子移到组件的顶层,在任何可能返回值的条件之上。
    • 总结

问题背景

apm报错:Minified React error #310
https://reactjs.org/docs/error-decoder.html/?invariant=310
React报错#310复盘小结_第1张图片

当我们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会产生"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在多个useStateuseEffect调用之间保留钩子的状态。

你可能感兴趣的:(react,react.js,javascript,前端)