带有 NextJS 路由器的 MUI 导航选项卡 - 添加图标会导致未定义的 TypeError

发布于 2025-01-11 16:32:11 字数 6237 浏览 0 评论 0 原文

我已经实现了类似于此处NavTabs >,但使用 next/router

问题:如果您向任何选项卡添加图标,应用程序就会崩溃。

实时 Codesandbox 复制

在 Codesandbox 中,单击任一选项卡都会导致错误。如果您随后从两个 Tab 组件中删除 icon 属性,它将正常工作。

完整的代码和 MUI 文档可以在下面找到。

输入图片此处描述

其他详细信息和日志记录错误:

如果您不使用标签而仅使用图标,如下所示:

import HomeIcon from '@mui/icons-material/Home'
<LinkTab href="/drafts" icon = {<HomeIcon />} />

那么单击可能会导致 TypeError 扔向router.push(e.target.href)。如果您同时使用 labeliconaria-label 这三个选项,也会出现同样的错误。

handleTabChange 方法中,我记录了事件并发现了变化。

成功点击不带 iconLinkTab 时发生的事件:

SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: 
PointerEvent, target: a.MuiButtonBase-root.MuiTab-root.MuiTab-textColorPrimary.css-knz1ty- 
MuiButtonBase-root-MuiTab-root, …}

不成功点击带有 iconLinkTab 时发生的事件/code>:

SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: 
PointerEvent, target: path, …}

目标有时会更改为 path,据我所知,只有当 LinkTab 中存在图标时才会发生这种情况。

我有什么想法可以解决这个问题吗?

替代尝试#1:

下面的实现更新了选项卡的状态而不会崩溃,但它实际上不会导航到新页面。如果您尝试将 onClick 添加到各个 Tab 组件,则 Tabs 状态将不会按预期更新。

<Tabs value = {value} onChange = {(e, v) => handleChange(e, v)} aria-label = 'nav'>
  <Tab icon = {<HomeIcon />} aria-label = 'home' value = {pages[0]} to = {pages[0]} component = {Link} />
  <Tab icon = {<AboutIcon />} aria-label = 'about' value = {pages[1]} to = {pages[1]} component = {Link} />
</Tabs>

替代尝试#2:

以下实现也会导致相同的TypeError

import TabContext from '@mui/lab/TabContext'
import TabList from '@mui/lab/TabList'

<TabContext value = {value}>
  <TabList value = {value} onChange = {(e, v) => handleChange(e, v)} centered aria-label = 'nav'>
    <LinkTab icon = {<HomeIcon />} href = {pages[0]} aria-label = 'home' />
    <LinkTab icon = {<InfoIcon />} href = {pages[1]} aria-label = 'about' />
  </TabList>
</TabContext>

错误日志

Uncaught TypeError: Cannot read properties of undefined (reading 'auth')
    at Object.formatUrl (format-url.js?7b53:32:11)
    at Object.formatWithValidation (utils.js?e7ff:109:28)
    at resolveHref (router.js?8684:190:69)
    at prepareUrlAs (router.js?8684:247:38)
    at Router.push (router.js?8684:532:26)
    at Object.instance.<computed> [as push] (router.js?31fc:150:20)
    at handleTabChange (NavTabs.js?141c:61:12)
    at onClick (NavTabs.js?141c:69:25)
    at handleClick (Tab.js?4f19:161:1)
    ...

完整代码(也可以在Codesandbox上找到) :

index.js:

import { useRouter } from "next/router";
import { useState } from "react";
import Box from "@mui/material/Box";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { styled } from "@mui/material/styles";
import HomeIcon from "@mui/icons-material/Home";
import InfoIcon from "@mui/icons-material/Info";

const StyledTab = styled(Tab)(({ theme }) => ({
  color: theme.palette.text.primary,
  "&:hover": { color: theme.palette.primary.main }
}));

const LinkTab = (props) => {
  const router = useRouter();

  const handleTabChange = (e) => {
    console.log(e);
    e.preventDefault();
    router.push(e.target.href);
  };

  return (
    <StyledTab
      component="a"
      disableFocusRipple
      disableRipple
      onClick={(e) => handleTabChange(e)}
      {...props}
    />
  );
};

export const NavTabs = () => {
  const router = useRouter();

  const pages = ["/", "/about"];
  const [value, setValue] = useState(pages.indexOf(router.route));

  const handleChange = (e, v) => {
    setValue(v);
  };

  return (
    <Box>
      <Tabs value={value} onChange={(e, v) => handleChange(e, v)} centered>
        <LinkTab
          label="Home"
          href={pages[0]}
          icon={<HomeIcon />}
          aria-label="home"
        />
        <LinkTab
          label="About"
          href={pages[1]}
          icon={<InfoIcon />}
          aria-label="about"
        />
      </Tabs>
    </Box>
  );
};

export default () => (
  <div>
    <NavTabs />
  </div>
);

about.js:

import NavTabs from "./index";

export default () => (
  <div>
    <NavTabs />
  </div>
);

MUI 文档:

https://mui.com/components/tabs/#api

https://mui.com/components/tabs/#nav-tabs

https://mui.com/components/tabs/#icon-tabs

https://mui.com/components/tabs/#experimental-api

https://mui.com/components/tabs/#third-party-routing-library

https://mui.com/guides/routing/#tabs

https://mui.com/guides/routing/#next-js

I've implemented NavTabs similar to the implementation found here, but using next/router.

Problem: The app crashes if you add an icon to any Tab.

Live Codesandbox reproduction

In the Codesandbox, clicking either tab will cause the error. If you then remove the icon property from both Tab components, it will work without issue.

The full code and MUI documentation can be found below.

enter image description here

Additional detail and logging errors:

If you were to use no label and only an icon, like this:

import HomeIcon from '@mui/icons-material/Home'
<LinkTab href="/drafts" icon = {<HomeIcon />} />

Then a click can result in a TypeError thrown at router.push(e.target.href). The same error occurs if you use all three of label, icon, and aria-label.

In the handleTabChange method, I logged the event and found what's changing.

The event from a successful click on a LinkTab with no icon:

SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: 
PointerEvent, target: a.MuiButtonBase-root.MuiTab-root.MuiTab-textColorPrimary.css-knz1ty- 
MuiButtonBase-root-MuiTab-root, …}

The event from an unsuccessful click on a LinkTab with an icon:

SyntheticBaseEvent {_reactName: 'onClick', _targetInst: null, type: 'click', nativeEvent: 
PointerEvent, target: path, …}

It seems that the target changes to path sometimes, and as far as I can tell this only happens when an icon is present in the LinkTab.

Any ideas how I can get around this?

Alternative attempt #1:

The below implementation updates the state of the Tabs without crashing, but it won't actually navigate to a new page. If you try to add onClick to the individual Tab components, then the Tabs state won't update as expected.

<Tabs value = {value} onChange = {(e, v) => handleChange(e, v)} aria-label = 'nav'>
  <Tab icon = {<HomeIcon />} aria-label = 'home' value = {pages[0]} to = {pages[0]} component = {Link} />
  <Tab icon = {<AboutIcon />} aria-label = 'about' value = {pages[1]} to = {pages[1]} component = {Link} />
</Tabs>

Alternative attempt #2:

The below implementation also results in the same TypeError:

import TabContext from '@mui/lab/TabContext'
import TabList from '@mui/lab/TabList'

<TabContext value = {value}>
  <TabList value = {value} onChange = {(e, v) => handleChange(e, v)} centered aria-label = 'nav'>
    <LinkTab icon = {<HomeIcon />} href = {pages[0]} aria-label = 'home' />
    <LinkTab icon = {<InfoIcon />} href = {pages[1]} aria-label = 'about' />
  </TabList>
</TabContext>

Error log:

Uncaught TypeError: Cannot read properties of undefined (reading 'auth')
    at Object.formatUrl (format-url.js?7b53:32:11)
    at Object.formatWithValidation (utils.js?e7ff:109:28)
    at resolveHref (router.js?8684:190:69)
    at prepareUrlAs (router.js?8684:247:38)
    at Router.push (router.js?8684:532:26)
    at Object.instance.<computed> [as push] (router.js?31fc:150:20)
    at handleTabChange (NavTabs.js?141c:61:12)
    at onClick (NavTabs.js?141c:69:25)
    at handleClick (Tab.js?4f19:161:1)
    ...

Full code (also found on Codesandbox):

index.js:

import { useRouter } from "next/router";
import { useState } from "react";
import Box from "@mui/material/Box";
import Tabs from "@mui/material/Tabs";
import Tab from "@mui/material/Tab";
import { styled } from "@mui/material/styles";
import HomeIcon from "@mui/icons-material/Home";
import InfoIcon from "@mui/icons-material/Info";

const StyledTab = styled(Tab)(({ theme }) => ({
  color: theme.palette.text.primary,
  "&:hover": { color: theme.palette.primary.main }
}));

const LinkTab = (props) => {
  const router = useRouter();

  const handleTabChange = (e) => {
    console.log(e);
    e.preventDefault();
    router.push(e.target.href);
  };

  return (
    <StyledTab
      component="a"
      disableFocusRipple
      disableRipple
      onClick={(e) => handleTabChange(e)}
      {...props}
    />
  );
};

export const NavTabs = () => {
  const router = useRouter();

  const pages = ["/", "/about"];
  const [value, setValue] = useState(pages.indexOf(router.route));

  const handleChange = (e, v) => {
    setValue(v);
  };

  return (
    <Box>
      <Tabs value={value} onChange={(e, v) => handleChange(e, v)} centered>
        <LinkTab
          label="Home"
          href={pages[0]}
          icon={<HomeIcon />}
          aria-label="home"
        />
        <LinkTab
          label="About"
          href={pages[1]}
          icon={<InfoIcon />}
          aria-label="about"
        />
      </Tabs>
    </Box>
  );
};

export default () => (
  <div>
    <NavTabs />
  </div>
);

about.js:

import NavTabs from "./index";

export default () => (
  <div>
    <NavTabs />
  </div>
);

MUI documentation:

https://mui.com/components/tabs/#api

https://mui.com/components/tabs/#nav-tabs

https://mui.com/components/tabs/#icon-tabs

https://mui.com/components/tabs/#experimental-api

https://mui.com/components/tabs/#third-party-routing-library

https://mui.com/guides/routing/#tabs

https://mui.com/guides/routing/#next-js

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文