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
相关产品推荐
相关产品推荐

