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

Next.js组件中Preact Signals导航状态异常问题求助

解决Next.js中Preact Signals控制移动端导航失效问题

问题背景

在Next.js项目的MobileNav组件中,使用@preact/signals替换原useState管理导航弹窗状态时出现以下问题:

  • 仅在打开按钮的isNavOpen.value = true;行报错,其他修改信号值的位置无报错
  • 尝试类型断言或抽离状态修改函数后,报错消失,但导航完全失效,点击打开/关闭按钮均无反应

错误原因

  1. 信号状态读取错误:直接使用!isNavOpen判断状态,而Preact Signal是一个包装对象,必须读取.value属性才能获取实际布尔值。直接判断对象会始终返回true(对象为真值),导致打开按钮永远不渲染,导航弹窗逻辑完全混乱。
  2. 不必要的类型断言破坏了信号的内部订阅机制,导致组件无法响应信号值的变化。

解决方案

  1. 所有判断信号状态的位置,必须通过.value属性读取实际值
  2. 移除多余的类型断言,保留信号的原始定义
  3. 确保所有修改状态的逻辑正确设置.value属性

修正后的完整代码

"use client";
import React from "react";
import { AnimatePresence, motion } from "framer-motion";
import { Route, routeCollection } from "../../data/route";
import { IonIcon } from "@ionic/react";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
  linkAppearAnimationVariant,
  linkContainerAnimationVariants,
  navScreenVariant,
} from "../../animation/navbarVariants";
import { signal } from "@preact/signals";

const isNavOpen = signal<boolean>(false);

const MobileNav = () => {
  const pathname = usePathname();

  return (
    <>
      {!isNavOpen.value && (
        <div
          className="fixed right-12 top-12 md:hidden text-neutral-50"
          onClick={() => {
            isNavOpen.value = true;
          }}
        >
          Open
        </div>
      )}
      <AnimatePresence>
        {isNavOpen.value && (
          <motion.div
            variants={navScreenVariant}
            initial="initial"
            animate="animate"
            exit="exit"
            className="w-full h-full flex flex-col justify-between absolute origin-top bg-main backdrop-blur-md md:hidden text-neutral-50 p-8"
          >
            <motion.div className="flex justify-between p-4 items-center">
              <motion.p className="font-bold text-lg">Ayush</motion.p>
              <motion.p
                onClick={() => {
                  isNavOpen.value = false;
                }}
              >
                Close
              </motion.p>
            </motion.div>
            <motion.div
              variants={linkContainerAnimationVariants}
              initial="initial"
              animate="animate"
              exit="initial"
              className="flex flex-col justify-between items-center gap-4"
            >
              {routeCollection.map((eachRoute, index) => (
                <div className="overflow-hidden" key={index}>
                  <LinkRender
                    eachRoute={eachRoute}
                    pathName={pathname}
                    closeTab={() => {
                      isNavOpen.value = false;
                    }}
                  />
                </div>
              ))}
            </motion.div>
            <motion.a
              href="mailto:ayushmondal001@gmail.com"
              className="underline underline-offset-2 text-center"
            >
              Contact Me
            </motion.a>
          </motion.div>
        )}
      </AnimatePresence>
    </>
  );
};

const LinkRender = ({
  eachRoute,
  pathName,
  closeTab,
}: {
  eachRoute: Route;
  pathName: string;
  closeTab: () => void;
}) => {
  return (
    <motion.div variants={linkAppearAnimationVariant} onClick={closeTab}>
      <Link href={eachRoute.address}>
        <motion.div
          className={`flex gap-2 items-center font-bold text-md ${
            pathName === eachRoute.address ? "text-extraLight" : ""
          }`}
        >
          <IonIcon icon={eachRoute.iconText}></IonIcon>
          <span>{eachRoute.name}</span>
        </motion.div>
      </Link>
    </motion.div>
  );
};

export default MobileNav;

关键修改点

  • 将{!isNavOpen && ...}改为{!isNavOpen.value && ...}
  • 将{isNavOpen && ...}改为{isNavOpen.value && ...}
  • 移除了类型断言,恢复信号的原始定义const isNavOpen = signal<boolean>(false);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 14:15:22