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渲染。
- 新建
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> ); }
- 修改
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> ); }
- 在
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
相关产品推荐
相关产品推荐

