React报错#310复盘小结+hooks使用的场景+调用原理

问题背景

apm报错:Minified React error #310
https://reactjs.org/docs/error-decoder.html/?invariant=310
[图片]

当我们有条件地调用一个钩子或在所有钩子运行之前提前返回时,会产生"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 进行关联。

那如果在正常程序中插入一个if语句呢?如下:

const App = () => {
    const [loading, setLoading] = useState(false)
    useEffect(函数1)
    if (条件) {
      return ()
    }
    const [test, setTest] = useState('name')
}

If条件的存在导致程序提前终止,不再执行下方语句(而下方又存在一些hook),导致两种渲染情况不一致:

// 没进入到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>
  );
}

3. React函数组件或自定义钩子中只在调用Hook

// 在普通函数中不要使用hook
const AppContent = () => {
  const [counter, setCounter] = useState(0);

  return (
    …………
  );
}
export const App = {
  dataIndex: 'app',
- render: AppContent,
+ render: () => <AppContent />
}

总结

  • 只从React函数组件或自定义钩子中调用Hook
  • 只在最顶层使用 Hook
  • 不要在循环,条件或嵌套函数中调用 Hook
  • 确保总是在你的 React 函数的最顶层以及任何 return 之前使用 Hook

这有助于React在多个useStateuseEffect调用之间保留钩子的状态。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
React 18全家桶是React.js的最新版本,它引入了很多新特性和改进,使得开发更加方便和高效。其中,Hooks是React 16.8版本引入的一项重要特性,它允许我们在无需编写类组件的情况下,在函数式组件中使用状态和其他React功能。 React 18全家桶Hooks项目实战网盘是一个基于React 18全家桶的实际开发项目,旨在通过实际开发来学习和掌握React 18全家桶及Hooks的使用。 在这个项目中,我们可以通过网盘将文件上传、下载和分享给其他用户。首先,我们可以使用React 18全家桶的新特性来创建一个界面,例如使用React函数组件和Hooks来管理界面的状态、处理用户输入以及渲染文件列表等。我们可以使用React Router来管理不同页面之间的导航,并使用React Context来共享全局的状态和数据。 对于文件的上传和下载功能,我们可以利用React 18全家桶提供的新API,例如使用React Concurrent Mode来提高文件上传和下载的性能。同时,我们可以使用React Query来管理文件的后台数据请求和状态更新,以及使用React Hook Form来处理表单的数据验证和提交。 在项目中,我们可以使用React 18全家桶的新特性来实现一些高级的功能,例如使用React Server Components来实现服务器端渲染和实时数据更新,或使用React Fast Refresh来提高开发和调试的效率。 通过参与React 18全家桶Hooks项目实战网盘,我们可以深入了解并熟练掌握React 18全家桶及Hooks的使用。这将有助于我们在实际的React项目开发中提高开发效率和代码质量。同时,我们也可以通过参与项目来拓展我们的React技术栈,并与其他开发者共同学习和交流。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你脸上有BUG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值