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

Next.js Page Router页面跳转时Context状态未重置的解决方案咨询

问题场景代码

pages/_app.tsx

import { NextPage } from "next";

type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  getLayout?: (page: ReactElement) => ReactNode; 
};

type AppPropsWithLayout = AppProps & {
  Component: NextPageWithLayout;
};

export function App({ Component, ...rest }: AppPropsWithLayout) {
    return (
       <SidebarContextProvider>
        {getLayout(<Component {...pageProps} />, rest.pageProps)}
      </SidebarContextProvider>
    )
}

context/SidebarContext.tsx

import { createContext, useContext, useState } from "react";

const SidebarContext = createContext({} as any);

export const SidebarContextProvider = ({ children }: { children: React.ReactNode}) => {
    const [isMenuOpen, setIsMenuOpen] = useState(true);
    const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
    console.log(isMobileMenuOpen);

    return (
      <SidebarContext.Provider
        value={{
          isMenuOpen,
          setIsMenuOpen,
          isMobileMenuOpen,
          setIsMobileMenuOpen,
        }}
      >
        {children}
      </SidebarContext.Provider>
    );
};

export const useSidebarContext = () => useContext(SidebarContext);

layouts/RootLayout.tsx

function RootLayout() {
    const {
      isMenuOpen,
      setIsMenuOpen,
      isMobileMenuOpen,
      setIsMobileMenuOpen
    } = useSidebarContext()
}

function getRootLayout(page: ReactElement, props?: any) {
  return <RootLayout {...props}>{page}</RootLayout>;
}

export default RootLayout;
export { RootLayout, getRootLayout };

components/Sidebar.tsx

import Link from "next/link";

<Link className="nav-link" href={`/about`}>About</Link>
<Link className="nav-link" href={`/contact-us`}>Contact Us</Link>

pages/index.tsx

Homepage.getLayout = getRootLayout;
问题描述

在该Next.js Page Router项目中,点击Next.js的Link组件跳转至其他页面时,SidebarContext中的变量isMobileMenuOpen无法重置为初始值false,跳转后仍保持true状态。项目采用getLayout方式渲染布局,需要实现页面跳转时重置Context状态。

解决方案

方案一:在Context中全局监听路由变化

修改context/SidebarContext.tsx,引入useRouter监听路由完成事件,自动重置移动端菜单状态:

import { createContext, useContext, useState, useEffect } from "react";
import { useRouter } from "next/router";

// 明确类型定义,避免any类型
const SidebarContext = createContext({} as {
  isMenuOpen: boolean;
  setIsMenuOpen: (value: boolean) => void;
  isMobileMenuOpen: boolean;
  setIsMobileMenuOpen: (value: boolean) => void;
});

export const SidebarContextProvider = ({ children }: { children: React.ReactNode}) => {
    const [isMenuOpen, setIsMenuOpen] = useState(true);
    const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
    const router = useRouter();

    useEffect(() => {
        // 路由跳转完成后重置移动端菜单
        const handleRouteChange = () => {
            setIsMobileMenuOpen(false);
        };

        router.events.on('routeChangeComplete', handleRouteChange);
        // 组件卸载时移除监听,避免内存泄漏
        return () => {
            router.events.off('routeChangeComplete', handleRouteChange);
        };
    }, [router]);

    return (
      <SidebarContext.Provider
        value={{
          isMenuOpen,
          setIsMenuOpen,
          isMobileMenuOpen,
          setIsMobileMenuOpen,
        }}
      >
        {children}
      </SidebarContext.Provider>
    );
};

export const useSidebarContext = () => useContext(SidebarContext);

方案二:在RootLayout中监听路由变化

如果不想修改Context核心逻辑,可以在布局组件中添加路由监听:

import { useEffect } from "react";
import { useRouter } from "next/router";
import { useSidebarContext } from "../context/SidebarContext";

function RootLayout({ children }: { children: React.ReactNode }) {
    const { setIsMobileMenuOpen } = useSidebarContext();
    const router = useRouter();

    useEffect(() => {
        const resetMobileMenu = () => {
            setIsMobileMenuOpen(false);
        };

        router.events.on('routeChangeComplete', resetMobileMenu);
        return () => router.events.off('routeChangeComplete', resetMobileMenu);
    }, [router, setIsMobileMenuOpen]);

    return (
        // 这里补充你的RootLayout原有结构,比如包含Sidebar等
        <div className="layout-container">
            {/* 你的Sidebar组件 */}
            {children}
        </div>
    );
}

function getRootLayout(page: React.ReactElement, props?: any) {
  return <RootLayout {...props}>{page}</RootLayout>;
}

export default RootLayout;
export { RootLayout, getRootLayout };

方案三:点击Link时手动重置

如果只需要在特定导航链接点击时重置状态,可以在Sidebar组件中给Link添加点击事件:

import Link from "next/link";
import { useSidebarContext } from "../context/SidebarContext";

const Sidebar = () => {
    const { setIsMobileMenuOpen } = useSidebarContext();

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

    return (
        <nav>
            <Link 
                className="nav-link" 
                href={`/about`} 
                onClick={handleNavClick}
            >
                About
            </Link>
            <Link 
                className="nav-link" 
                href={`/contact-us`} 
                onClick={handleNavClick}
            >
                Contact Us
            </Link>
        </nav>
    );
};

export default Sidebar;

核心原因说明

Next.js的Link组件是客户端导航,不会重新挂载App组件和Context Provider,因此Context中的状态不会自动重置。以上方案通过监听路由事件或手动触发的方式,强制在页面跳转时重置目标状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 23:51:00