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

NextJS 13:Root Layout作为服务端组件时createContext报错问询

报错原因与解决方案

报错信息

Error: createContext only works in Client Components. Add the "use client" directive at the top of the file to use it.

相关代码

// layout.tsx
import Layout from "./components/common/Layout";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Layout>{children}</Layout>
      </body>
    </html>
  );
}
// Layout.tsx

"use client";

import React, { useState, ReactNode } from "react";
import Nav from "@app/components/common/Nav";
import { Global, css } from "@emotion/react";
import { PageContextProvider } from "store/pageContext";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export default function Layout({ children }: { children: ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            refetchOnWindowFocus: false,
            staleTime: 5000,
          },
        },
      })
  );
  return (
    <div>
      <QueryClientProvider client={queryClient}>
        <Global styles={reset} />
        <PageContextProvider>
          <Nav />
          <div>{children}</div>
        </PageContextProvider>
      </QueryClientProvider>
    </div>
  );
}
// pageContext.tsx

"use client";

import React, { createContext, useState, useRef } from "react";

interface PageContextType {
  currentPage: number;
  setCurrentPage: React.Dispatch<React.SetStateAction<number>>;
  sectionsRef: React.MutableRefObject<(HTMLElement | null)[]>;
}

export const PageContext = createContext<PageContextType>({
  currentPage: 0,
  setCurrentPage: () => {},
  sectionsRef: {} as React.MutableRefObject<(HTMLElement | null)[]>,
});

export const PageContextProvider = ({
  children,
}: {
  children: React.ReactNode;
}) => {
  const [currentPage, setCurrentPage] = useState<number>(0);
  const sectionsRef = useRef<(HTMLDivElement | null)[]>([]);

  return (
    <PageContext.Provider value={{ currentPage, setCurrentPage, sectionsRef }}>
      {children}
    </PageContext.Provider>
  );
};

问题诉求

希望将layout.tsx作为服务端组件并添加metadata,但不添加"use client"就触发上述报错,该怎么解决?


报错原因

在Next.js App Router中,组件默认是服务端组件,这类组件不能使用createContext、useState这类客户端专属的React API。

你的RootLayout(layout.tsx)是服务端组件,它导入了带"use client"的Layout组件。虽然Layout本身标记了客户端组件,但依赖链中PageContextProvider用到的createContext属于客户端API,在服务端解析依赖时,可能出现上下文检查异常,错误地将报错抛到了服务端组件层面。

解决方案

方案1:彻底隔离客户端逻辑

把所有用到客户端API的上下文提供者封装到独立的客户端组件中,让服务端组件只负责静态内容和metadata渲染。

  1. 新建Providers.tsx,封装所有客户端上下文:
// Providers.tsx
"use client";

import React, { useState } from "react";
import { PageContextProvider } from "store/pageContext";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";

export default function Providers({ children }: { children: React.ReactNode }) {
  const [queryClient] = useState(
    () =>
      new QueryClient({
        defaultOptions: {
          queries: {
            refetchOnWindowFocus: false,
            staleTime: 5000,
          },
        },
      })
  );
  
  return (
    <QueryClientProvider client={queryClient}>
      <PageContextProvider>
        {children}
      </PageContextProvider>
    </QueryClientProvider>
  );
}
  1. 修改Layout.tsx,引入新的Providers组件:
// Layout.tsx
"use client";

import React, { ReactNode } from "react";
import Nav from "@app/components/common/Nav";
import { Global, css } from "@emotion/react";
import Providers from "./Providers";

export default function Layout({ children }: { children: ReactNode }) {
  return (
    <div>
      <Global styles={reset} />
      <Providers>
        <Nav />
        <div>{children}</div>
      </Providers>
    </div>
  );
}
  1. 在layout.tsx中添加metadata,保持服务端组件身份:
// layout.tsx
import Layout from "./components/common/Layout";
import type { Metadata } from "next";

// 服务端渲染的metadata
export const metadata: Metadata = {
  title: "自定义网站标题",
  description: "自定义网站描述",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        <Layout>{children}</Layout>
      </body>
    </html>
  );
}

方案2:检查导入路径与缓存

  • 确认PageContextProvider的导入路径正确,指向的pageContext.tsx确实在顶部添加了"use client"指令。
  • 重启Next.js开发服务器,清除依赖缓存,避免旧的解析结果导致异常。

内容的提问来源于stack exchange,提问作者Jongsung Jung

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 00:52:41