Next.js路由渲染取消错误:基础鉴权与路由保护问题排查
Next.js "Cancel rendering route" 错误排查与修复
问题根源
- 状态不共享:
useUserSession被重复调用(_app.tsx、MainLayout、LoginLayout各初始化一次),每个调用都会创建独立的状态实例。点击登录后修改的是LoginLayout内的局部状态,而Home页面读取的是_app.tsx中的初始未登录状态,触发循环跳转,最终导致路由渲染被取消。 - 重复包裹Context Provider:每个布局组件都重新套了
userSession.Provider,覆盖了全局状态,进一步加剧状态不一致问题。 - 路由跳转逻辑缺陷:Home页面的
useEffect跳转基于不一致的状态判断,引发循环跳转触发Next.js的路由渲染取消机制。
修复方案
1. 统一全局状态管理
仅在_app.tsx中初始化useUserSession,通过Context将状态全局传递,所有子组件通过useContext获取状态,不再单独调用useUserSession。
2. 移除布局中的重复Provider
删除MainLayout和LoginLayout内的userSession.Provider,仅保留_app.tsx中的全局Provider。
3. 修正路由保护逻辑
确保跳转判断基于全局统一状态,避免循环跳转。
修改后的关键代码
_app.tsx(全局状态入口)
import { AppPropsWithLayout } from 'next/app'; import useUserSession from '../hooks/useUserSession'; import userSession from '../contexts/userSession'; export default function App({ Component, pageProps }: AppPropsWithLayout) { const getLayout = Component.getLayout ?? ((page) => page); const { username, userLoggedIn, login, loggout } = useUserSession(); return getLayout( <userSession.Provider value={{ isLoggedIn: userLoggedIn, username, login, loggout }}> <Component {...pageProps} /> </userSession.Provider> ); }
MainLayout.tsx(移除重复Provider)
import { ReactNode } from 'react'; function MainLayout({ children }: { children: ReactNode }) { return ( <> <PageHeader /> <UserSideBar /> <div className="pageContent"> {children} </div> </> ); } export default MainLayout;
LoginLayout.tsx(移除重复Provider)
import { ReactNode } from 'react'; function LoginRegisterLayout({ children }: { children: ReactNode }) { return ( <> <PageHeader /> {children} </> ); } export default LoginRegisterLayout;
Home.tsx(修正路由保护)
import { useEffect, useContext } from 'react'; import { useRouter } from 'next/router'; import userSession from '../contexts/userSession'; const Home: PageWithLayout = () => { const userSessionData = useContext(userSession); const router = useRouter(); useEffect(() => { if (!userSessionData.isLoggedIn) { // 使用replace避免历史记录堆积 router.replace('/login'); } }, [userSessionData.isLoggedIn, router]); // 原页面内容不变... }; Home.getLayout = function getLayout(page: ReactElement) { return <MainLayout>{page}</MainLayout>; }; export default Home;
可选:优化useUserSession初始状态
import { useRouter } from "next/router"; import { useCallback, useState } from "react"; const useUserSession = () => { // 明确初始类型,避免undefined导致判断错误 const [username, setUsername] = useState<string>(''); const [userLoggedIn, setUserLoggedIn] = useState<boolean>(false); const router = useRouter(); const login = useCallback(() => { setUsername('Admin'); setUserLoggedIn(true); router.push("/"); }, [router]); const loggout = useCallback(() => { setUsername(''); setUserLoggedIn(false); router.push("/login"); }, [router]); return { username, userLoggedIn, login, loggout }; }; export default useUserSession;
内容的提问来源于stack exchange,提问作者mitas1c
相关产品推荐
相关产品推荐

