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

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本身处于上下文范围内。

  1. 修改根布局文件(通常是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>
  );
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 06:05:02