You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Next.js组件无法作为JSX组件使用,移动端菜单故障求助

解决Next.js中移动端菜单组件报错及层级问题

错误根源

  1. 组件无JSX返回:Navbar组件内部定义了MobileMenu,但函数末尾未返回任何JSX内容,导致组件无法被正确渲染,触发"Cannot be used as a JSX component"错误。
  2. 冗余依赖与语法错误:导入了多个未使用的模块(如useEffect、react-spring相关API),可能引发编译冲突;部分类名存在拼写错误(如dark:text-gray-40应为dark:text-gray-400)。

修复步骤

1. 让Navbar返回MobileMenu组件

在Navbar函数末尾添加返回语句,确保组件输出合法JSX:

export default function Navbar(props) {
  // ... 原有代码 ...

  const MobileMenu = () => {
    // ... MobileMenu内部代码 ...
  }

  // 新增:返回MobileMenu组件
  return <MobileMenu />;
}

2. 修复类名拼写错误

找到Disclosure.Panel内的dark:text-gray-40,修改为完整的dark:text-gray-400:

<div className="text-xl font-serif hover:text-blue-500 dark:text-gray-400 order-2 -ml-4 mt-4 flex w-full flex-col items-center justify-start md:hidden">

3. 移除未使用的依赖

删除代码中未用到的导入,减少潜在编译冲突:

// 保留必要导入,移除冗余项
import { Fragment, useState } from "react";
import { Disclosure } from "@headlessui/react";
import Container from "@/components/container";
import Link from "next/link";
import Image from "next/image";
import { urlForImage } from "@/lib/sanity/image";

4. 菜单层级优化(无需useEffect)

当前代码中遮罩层已使用fixed inset-0 bg-black bg-opacity-50 z-50,只要页面其他元素的z-index不超过50,就能确保菜单显示在最上层。若需动态控制层级,可通过isMobileMenuOpen状态切换类名,现有逻辑已满足基础需求。

修改后的完整代码

/* @jsxImportSource next/client */

import { Fragment, useState } from "react";
import { Disclosure } from "@headlessui/react";
import Container from "@/components/container";
import Link from "next/link";
import Image from "next/image";
import { urlForImage } from "@/lib/sanity/image";

export default function Navbar(props) {
  const leftmenu = [
    {
      label: "Home",
      href: "/"
    },
    {
      label: "About",
      href: "/about"
    },
    {
      label: "Contact",
      href: "/contact"
    },
    {
      label: "Archive",
      href: "/archive"
    },
  ];

  const MobileMenu = () => {
    const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);

    const handleMobileMenuToggle = () => {
      setIsMobileMenuOpen(!isMobileMenuOpen);
    };

    const handleMobileMenuLinkClick = () => {
      setIsMobileMenuOpen(false);
    };

    const mobilemenu = [...leftmenu];

    return (
      <Container>
        {isMobileMenuOpen && (
          <div className="fixed inset-0 bg-black bg-opacity-50 z-50" onClick={handleMobileMenuToggle} />
        )}
        <nav>
          <Disclosure>
            {({ open }) => (
              <>
                <div className="flex flex-wrap justify-between md:flex-nowrap md:gap-10 h-24 border-b-2 border-gray-100">
                  <div className="flex w-full items-center justify-between">
                    <Link href="/" className="w-48 dark:hidden">
                      {props.logo ? (
                        <Image 
                          {...urlForImage(props.logo)}
                          alt="Logo"
                          priority={true}
                          sizes="(max-width: 1040px) 100vw, 400px"
                        />
                      ) : (
                        <span className="block text-center">
                          Ideato
                        </span>
                      )}
                    </Link>
                    <Link href="/" className="hidden w-48 dark:block">
                      {props.logoalt ? (
                        <Image
                          {...urlForImage(props.logoalt)}
                          alt="Logo"
                          priority={true}
                          sizes="(max-width: 1040px) 100vw, 400px"
                        />
                      ) : (
                        <span className="block text-center">
                          Ideato
                        </span>
                      )}
                    </Link>
                    <Disclosure.Button
                      aria-label="Toggle Menu"
                      className="ml-auto pt-4 h-full rounded-md px-2 py-1 text-gray-500 focus:text-blue-500 focus:outline-none dark:text-gray-300 md:hidden "  onClick={handleMobileMenuToggle} >
                      
                      <svg
                        className="h-8 w-8 fill-current"
                        xmlns="http://www.w3.org/2000/svg"
                        viewBox="0 0 24 24">
                        {open && (
                          <path
                            fillRule="evenodd"
                            clipRule="evenodd"
                            d="M18.278 16.864a1 1 0 0 1-1.414 1.414l-4.829-4.828-4.828 4.828a1 1 0 0 1-1.414-1.414l4.828-4.829-4.828-4.828a1 1 0 0 1 1.414-1.414l4.829 4.828 4.828-4.828a1 1 0 1 1 1.414 1.414l-4.828 4.829 4.828 4.828z"
                          />
                        )}
                        {!open && (
                          <path
                            fillRule="evenodd"
                            d="M4 5h16a1 1 0 0 1 0 2H4a1 1 0 1 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2zm0 6h16a1 1 0 0 1 0 2H4a1 1 0 0 1 0-2z"
                          />
                        )}
                      </svg>
                    </Disclosure.Button>
                  </div>
                  <div className="order-1 hidden w-full flex-col items-center justify-start md:order-none md:flex md:w-auto md:flex-1 md:flex-row md:justify-end pt-4">
                    {leftmenu.map((item, index) => (
                      <Fragment key={`${item.label}${index}`}>
                        {item.children && item.children.length > 0 ? (
                          {/* 若需要DropdownMenu,请补充组件实现 */}
                          <div key={`${item.label}${index}`}>Dropdown Menu Placeholder</div>
                        ) : (
                          <Link
                            href={item.href}
                            key={`${item.label}${index}`}
                            className="px-5 py-2 text-xl font-serif font-medium text-gray-600 hover:text-blue-500 dark:text-gray-400"
                            target={item.external ? "_blank" : ""}
                            rel={item.external ? "noopener" : ""}>
                            {item.label}
                          </Link>
                        )}
                      </Fragment>
                    ))}
                  </div>
                </div>
                <Disclosure.Panel>
                  <div className="text-xl font-serif hover:text-blue-500 dark:text-gray-400 order-2 -ml-4 mt-4 flex w-full flex-col items-center justify-start md:hidden">
                    {mobilemenu.map((item, index) => (
                      <Fragment key={`${item.label}${index}`}>
                        {item.children && item.children.length > 0 ? (
                          {/* 若需要移动端DropdownMenu,请补充组件实现 */}
                          <div key={`${item.label}${index}`}>Mobile Dropdown Menu Placeholder</div>
                        ) : (
                          <Link
                            href={item.href}
                            key={`${item.label}${index}`}
                            className="w-full px-5 py-2 font-medium text-gray-600 hover:text-blue-500 dark:text-gray-400"
                            target={item.external ? "_blank" : ""}
                            rel={item.external ? "noopener" : ""}
                            onClick={handleMobileMenuLinkClick}>
                            {item.label}
                          </Link>
                        )}
                      </Fragment>
                    ))}
                  </div>
                </Disclosure.Panel>
              </>
            )}
          </Disclosure>
        </nav>
      </Container>
    );
  }

  return <MobileMenu />;
}

注意:原代码中引用的DropdownMenu组件未定义,若需使用请补充实现,否则会引发新的错误,上述代码中已替换为占位符。

内容的提问来源于stack exchange,提问作者xBlady

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:25:54