【问题标题】:Rendered more hooks than during the previous render with custom hook比上一次使用自定义钩子渲染时渲染的钩子更多
【发布时间】:2021-11-02 08:55:22
【问题描述】:

在我的组件中使用自定义钩子返回值时,我不断收到“渲染的钩子比上一次渲染时更多”错误

钩子是这样的

import { useState, useEffect } from 'react';
import axios from 'axios';

const { REACT_APP_BASE_PATH } = process.env;

axios.defaults.baseURL = REACT_APP_BASE_PATH;

export const useAxios = (axiosParams, deps = []) => {
  const [response, setResponse] = useState({});
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(true);

  const fetchData = async (params) => {
    try {
      const result = await axios.request(params);
      setResponse(result.data);
    } catch (err) {
      setError(err);
    } finally {
      setLoading(false);
    }
  };

  useEffect(() => {
    fetchData(axiosParams);
  }, [...deps]);

  return { response, error, loading };
};

export default { useAxios };

组件如下所示:

import React from 'react';
import { useHistory, useLocation } from 'react-router-dom';
import { Box, Button, Typography } from '@material-ui/core';
import CircularProgress from '@material-ui/core/CircularProgress';
import { useAxios } from '../../helpers/hooks';
import { accountsListColumns, renderItems } from '../../helpers/constants';
import useStyles from '../styles';

export const SingleAccountPage = () => {
  const { search } = useLocation();
  const history = useHistory();
  const classes = useStyles();

  const {
    response = [],
    error,
    loading,
  } = useAxios({
    method: 'GET',
    url: `/account/findOne${search}`,
  });

  if (loading) return <CircularProgress />;
  if (error) return <h1>{error}</h1>;

  return (
    <Box className={classes.singlePageWrapper}>
      <Box className={classes.singlePageHeader}>
        <Typography variant={'h4'}>Account: {response.id}</Typography>
        <Button
          variant={'contained'}
          onClick={() => history.goBack()}
          className={classes.singlePageBackButton}
        >
          Go back
        </Button>
      </Box>
      {renderItems(accountsListColumns, response)}
    </Box>
  );
};

所以基本上这两行由于某种原因导致了问题

  if (loading) return <CircularProgress />;
  if (error) return <h1>{error}</h1>;

将来,我想有条件地渲染错误并加载组件,但现在由于某种原因,即使这样也行不通。

在错误消息中,它说

index.js:1 Warning: React has detected a change in the order of Hooks called by SingleAccountPage. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

   Previous render            Next render
   ------------------------------------------------------
1. useContext                 useContext
2. useContext                 useContext
3. useContext                 useContext
4. useDebugValue              useDebugValue
5. useContext                 useContext
6. useRef                     useRef
7. useRef                     useRef
8. useRef                     useRef
9. useMemo                    useMemo
10. useEffect                 useEffect
11. useEffect                 useEffect
12. useDebugValue             useDebugValue
13. useState                  useState
14. useState                  useState
15. useState                  useState
16. useEffect                 useEffect
17. undefined                 useContext
   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Uncaught Error: Rendered more hooks than during the previous render.

你能帮我解决这个问题吗?

【问题讨论】:

  • 这真的很奇怪。我建议您尝试在代码和框中创建一个最小的可重现示例并在那里产生相同的错误(这将帮助您和我们缩小问题的范围)。你有点让我有兴趣找出问题所在。目前,在您提供的代码中,我没有看到任何内容。 (您的早期退货声明没有任何问题,因此尽管删除它可以解决问题,但这可能不是这里的核心问题)。目前,我唯一的怀疑是你正在导入的 useStyles 钩子。你能分享一下代码吗?或者评论一下看看会发生什么?
  • 请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example

标签: reactjs react-hooks material-ui


【解决方案1】:

如果你在&lt;CircularProgress/&gt; 中有一个useEffect,这可能会导致问题,因为它仅在loadingtrue 时才会呈现。由于最初的加载是未定义的。

【讨论】:

  • 如果我在加载语句时使用注释并留下错误,我会收到一个错误“渲染的钩子比预期的少”=(不确定&lt;CircularProgress/&gt;是否有useEffect,因为它只是一个材质ui微调器组件
【解决方案2】:

这个问题通常是由早期的if 语句引起的。基本上对于所有渲染,您需要注册相同数量的钩子,并且注册的钩子顺序相同。

但是您的代码的简要外观似乎没有错,您的 if 语句就在 return 语句的上方。而且您已经缩小了问题陈述的范围。

  if (loading) return <CircularProgress />;

调试消息实际上已经告诉了我们一切,感谢您附上它,我以前从未真正看到过这条消息。很酷。

最后一行

17. undefined                 useContext

它说你应该有一个useContextCircularProgress 使用,但这次你没有。 useContext 主要用于主题化目的。

解决方案

现在我们知道发生了什么,让我们看看是否能解决它

  const spinner  = <CircularProgress />

  return (
    <>
     {loading && spinner}
     {!loading && (
        ... your rest of render ...
     )}
    </>

这样它总是被渲染,但只根据你的条件,它是否显示。

解决方案改进

当然这个解决方案破坏了你的代码流程,提前返回让你的代码非常整洁,但现在不行了,我们必须同时覆盖loading或不条件。

为了解决这个问题,也许回到你以前的方式。

  const spinner = <CircularProgress />
  if (loading) return spinner
  if (error) return <h1>{error}</h1>;

  ...

【讨论】:

  • 感谢您的详细解释!不幸的是,错误仍然存​​在,尽管现在微调器被渲染了一秒钟,之后,我得到了同样的错误。当我注释掉微调器并仅在错误语句时离开时,我也会收到一个错误,但现在它说渲染的钩子比以前少了
  • &lt;div&gt;spinner&lt;/div&gt;替换CicularProgress,看看你是否仍然遇到问题。如果您仍然遇到问题,那么这与微调器无关:)
  • 是的,仍然存在 =(
  • 那么这不是微调器问题:) 哈哈。您可以逐步注释掉更多代码,直到您回到一个有效的案例。
【解决方案3】:

有一个超级简单的解决方法。将所有函数、返回语句上方的钩子移动到任何地方。这样所有的钩子都在 return 语句之前呈现。这可能不利于性能,但目前肯定是一种解决方法。

【讨论】:

    猜你喜欢
    • 2019-12-24
    • 2021-01-06
    • 2019-10-02
    • 2021-12-01
    • 2021-05-31
    • 2020-11-26
    • 1970-01-01
    • 2021-10-12
    • 2019-09-01
    相关资源
    最近更新 更多