首页 > 解决方案 > 错误:每次调整屏幕大小后,呈现的挂钩数量少于预期

问题描述

我正在创建响应式导航栏,并且在渲染时效果很好。每次屏幕调整大小或切换到移动视图后,我都会收到错误消息:“错误:渲染的钩子比预期的少。这可能是由意外的提前返回语句引起的。”

  133 | 
  134 |  useEffect(() => {
  135 |    const setResponsiveness = () => {
> 136 |      return window.innerWidth < 900
  137 | ^         ? setState((prevState) => ({ ...prevState, mobileView: true }))
  138 |        : setState((prevState) => ({ ...prevState, mobileView: false }));
  139 |    };
  140 | 
  141 |    setResponsiveness();
  142 | 
> 143 |    window.addEventListener("resize", () => setResponsiveness());
  144 | ^ }, []);
  145 | 
  146 |  const displayDesktop = () => {

检查窗口宽度后,我只想将移动视图的状态更改为 false 或 true,然后调用 displayDesktop 或 displayMobile 函数。

包含最重要部分的简码:

export default function Navbar() {
  const {
    header,
    toolbar,
    drawerContainer,
    largeIcon,
    logo,
    desktopList,
  } = useStyles();

  const [state, setState] = useState({
    mobileView: false,
    drawerOpen: false,
  });

  const { mobileView, drawerOpen } = state;

  useEffect(() => {
    const setResponsiveness = () => {
      return window.innerWidth < 900
        ? setState((prevState) => ({ ...prevState, mobileView: true }))
        : setState((prevState) => ({ ...prevState, mobileView: false }));
    };

    setResponsiveness();

    window.addEventListener("resize", () => setResponsiveness());
  }, []);

  const displayDesktop = () => {
    return (
      <ThemeProvider theme={theme}>
        <Toolbar className={toolbar}>
          <div className={logo}>
            <MotorcycleRoundedIcon className={largeIcon} fontSize="large" />
            <Typography variant="h6" fontFamily="Arial" color="inherit" noWrap>
              Title
            </Typography>
          </div>
          <div className={desktopList}>
            {GetStatsButton()} {GetResultsButton()} {GetTablesButton()}
            {GetCalendarButton()}
          </div>
        </Toolbar>
      </ThemeProvider>
    );
  };

  const displayMobile = () => {
    const handleDrawerOpen = () =>
      setState((prevState) => ({ ...prevState, drawerOpen: true }));
    const handleDrawerClose = () =>
      setState((prevState) => ({ ...prevState, drawerOpen: false }));

    return (
      <ThemeProvider theme={theme}>
        <Toolbar>
          <IconButton
            {...{
              edge: "start",
              color: "inherit",
              "aria-label": "menu",
              "aria-haspopup": "true",
              onClick: handleDrawerOpen,
            }}
          >
            <MenuIcon />
          </IconButton>

          <Drawer
            {...{
              anchor: "top",
              open: drawerOpen,
              onClose: handleDrawerClose,
              variant: "temporary",
            }}
          >
            <div className={drawerContainer}>{getDrawerChoices()}</div>
          </Drawer>
          <MotorcycleRoundedIcon className={largeIcon} fontSize="large" />
          <Typography variant="h6" fontFamily="Arial" color="inherit" noWrap>
            Title
          </Typography>
        </Toolbar>
      </ThemeProvider>
    );
  };

  return (
    <header>
      <AppBar className={header}>
        {mobileView ? displayMobile() : displayDesktop()}
      </AppBar>
    </header>
  );
}

有人可以给我一个提示吗?我知道这与在条件内调用 Hooks 有关,但我不知道如何重新格式化它。

标签: reactjsmaterial-ui

解决方案


您正在从常规 JS 函数调用 React 钩子,这违反了钩子规则。例如,您在调用时这样做GetStatsButton(),因为所有这些组件都包含useSate调用。

您可以通过更改来解决此问题:

<div className={desktopList}>
  {GetStatsButton()} {GetResultsButton()} {GetTablesButton()}
  {GetCalendarButton()}
</div>

<div className={desktopList}>
  <GetStatsButton />
  <GetResultsButton />
  <GetTablesButton />
  <GetCalendarButton />
</div>

为了突出这些是 React 组件这一事实,最好从名称中删除Get 。

此外,您的StyledMenuItemandStyledMenu应该只在这些组件之外定义一次,然后您可以在需要它们的任何地方重用它们。

最后但并非最不重要的一点是,您可能想看看 MUI 的useMediaQuery以实现您想要的响应行为。


推荐阅读