NextJs 14中已定义Provider但useContext仍报错的问题求助
Next.js 14 上下文为空错误排查与解决方案
问题核心
在Next.js 14项目中使用React Context存储MUI抽屉开闭状态时,调用useAppContext始终抛出"上下文为空"的错误,即使已经定义了AppContextProvider并通过ContextLayout尝试包裹布局。
根本原因
你的GlobalLayout组件存在层级倒置问题:在GlobalLayout内部,你先调用了useAppContext(),然后才在return中用ContextLayout(即AppContextProvider)包裹子内容。这意味着GlobalLayout自身并没有被AppContextProvider覆盖,组件初始化时上下文还未被注入,自然会报错。
解决方案
方案1:调整ContextProvider的包裹层级
将ContextLayout的包裹逻辑移到GlobalLayout的外层,确保GlobalLayout本身处于上下文范围内。
- 修改根布局文件(通常是
app/layout.tsx):
// app/layout.tsx import GlobalLayout from '@/components/layouts/global.layout'; import ContextLayout from '@/components/layouts/context.layout'; export default function RootLayout({ children, }: { children: React.ReactNode; }) { return ( <html lang="zh-CN"> <body> <ContextLayout> <GlobalLayout>{children}</GlobalLayout> </ContextLayout> </body> </html> ); }
- 修改
global.layout.tsx,移除内部的ContextLayout:
// global.layout.tsx 'use client'; import { useAppContext } from '@/context/app.context'; // ...保留其他导入代码 const GlobalLayout: React.FC<GlobalLayoutProps> = props => { const { children } = props; const { isDrawerOpen, setIsDrawerOpen } = useAppContext(); const [drawerWidth, setDrawerWidth] = useState<string>('68px'); const isMobile: boolean = useMediaQuery('(max-width: 600px)'); const toggleDrawer = () => { setIsDrawerOpen(!isDrawerOpen); }; useEffect(() => { console.log(isDrawerOpen); setDrawerWidth(isDrawerOpen ? '240px' : '68px'); }, [isDrawerOpen]); // 直接返回布局内容,不再用ContextLayout包裹 return ( <Grid container> {/* ...保留原有的抽屉、导航栏、子内容渲染代码 */} </Grid> ); }; export default GlobalLayout;
方案2:将useAppContext移到子组件中
如果不想调整根布局,可以把依赖上下文的逻辑拆到GlobalLayout内部的子组件中,确保子组件被ContextLayout包裹:
// global.layout.tsx 'use client'; import { useAppContext } from '@/context/app.context'; // ...保留其他导入代码 const DrawerController = () => { const { isDrawerOpen, setIsDrawerOpen } = useAppContext(); const [drawerWidth, setDrawerWidth] = useState<string>('68px'); const isMobile: boolean = useMediaQuery('(max-width: 600px)'); const toggleDrawer = () => { setIsDrawerOpen(!isDrawerOpen); }; useEffect(() => { setDrawerWidth(isDrawerOpen ? '240px' : '68px'); }, [isDrawerOpen]); return ( <> {/* ...原有的抽屉、Fab按钮等渲染代码 */} </> ); }; const GlobalLayout: React.FC<GlobalLayoutProps> = props => { const { children } = props; return ( <ContextLayout> <Grid container> <DrawerController /> <Grid item sx={{ width: `calc(100% - ${drawerWidth})`, marginLeft: drawerWidth, }} > {children} </Grid> </Grid> </ContextLayout> ); }; export default GlobalLayout;
额外排查点
- 确保所有调用
useAppContext的组件都添加了'use client'指令,Next.js 14的服务器组件无法使用React Context API。 - 检查
AppContext的导入路径是否正确,避免因路径错误导入了不同的Context实例。 - 确认
AppContextProvider没有被条件渲染逻辑(如if判断)包裹,导致某些分支下上下文未被提供。
内容的提问来源于stack exchange,提问作者grimaldodev
相关产品推荐
相关产品推荐

