Next.js 13迁移报错:invariant expected app router to be mounted
解决Next.js 13迁移后调用
notFound()触发“invariant expected app router to be mounted”错误 问题根源
当页面调用notFound()时,App Router会中断当前路由的渲染流程,但你的根layout.tsx中存在异步数据获取逻辑,且传递给Providers组件的子元素里,有客户端组件提前调用了useRouter()——useRouter()依赖App Router的上下文环境,而notFound()触发时路由上下文尚未完全挂载,因此抛出错误。
具体解决方案
1. 调整根Layout的异步数据获取位置
根Layout是所有路由共享的,不要在根Layout中做异步数据获取并传递给Providers,把数据逻辑移到具体页面的Server Component中:
// 原错误的app/layout.tsx async function RootLayout({ children }) { const data = await fetch('/api/data'); // 根Layout异步获取数据 return ( <html> <body> <Providers data={data}>{children}</Providers> </body> </html> ); } // 修改后的app/layout.tsx function RootLayout({ children }) { // 移除根Layout的异步数据逻辑 return ( <html> <body> <Providers>{children}</Providers> </body> </html> ); } // app/(main)/page.tsx(Server Component) async function Page() { const data = await fetch('/api/data'); if (!data.ok) { notFound(); // 仅在页面层触发notFound,此时路由上下文已正常初始化 } return <MainPage data={await data.json()} />; }
2. 限制useRouter()的执行时机
如果Providers内的客户端组件必须使用useRouter(),通过useEffect延迟逻辑执行,确保路由上下文已挂载:
'use client'; import { useRouter, useEffect } from 'next/navigation'; function ClientComponentInProviders() { const router = useRouter(); useEffect(() => { // 仅在组件挂载后、路由上下文就绪时使用router const handleRouteChange = () => { console.log('路由变化', router.pathname); }; router.events.on('routeChangeComplete', handleRouteChange); return () => router.events.off('routeChangeComplete', handleRouteChange); }, [router]); return <div>客户端组件内容</div>; }
3. 用更安全的路由hooks替代useRouter()
如果只是获取路由信息(而非跳转),改用usePathname或useSearchParams,这些hooks在App Router中对上下文的依赖更稳定:
'use client'; import { usePathname } from 'next/navigation'; function ClientComponentInProviders() { const pathname = usePathname(); // 直接使用pathname,无需依赖useRouter return <div>当前路径:{pathname}</div>; }
4. 确保notFound()仅在Server Component中调用
notFound()是Server Component专属方法,不要在客户端组件中调用。如果客户端需要触发404,改用router.push('/404')或者自定义404组件。
内容的提问来源于stack exchange,提问作者Milos N.
相关产品推荐
相关产品推荐

