如何在Next 13 App目录的根布局中使用Context?
在Next.js 13 App目录的RootLayout中使用Context设置全局主题样式
要在RootLayout中通过Context控制body的全局主题样式,核心是用客户端组件实现ThemeProvider(服务端组件无法使用React状态/Context),并在Provider内部监听主题变化来动态修改body的样式类。以下是具体实现步骤:
1. 实现ThemeProvider(客户端组件)
在hooks/theme.tsx中编写主题Context的Provider,内部管理主题状态并同步更新body类名:
'use client'; import { createContext, useContext, useState, useEffect, ReactNode } from 'react'; type Theme = 'light' | 'dark'; type ThemeContextType = { theme: Theme; toggleTheme: () => void; }; const ThemeContext = createContext<ThemeContextType | undefined>(undefined); export default function ThemeProvider({ children }: { children: ReactNode }) { // 初始化主题:优先从localStorage读取,默认浅色 const [theme, setTheme] = useState<Theme>(() => { if (typeof window !== 'undefined') { return (localStorage.getItem('theme') as Theme) || 'light'; } return 'light'; }); // 主题变化时更新body类名和localStorage useEffect(() => { const body = document.body; body.classList.remove('light-theme', 'dark-theme'); body.classList.add(`${theme}-theme`); localStorage.setItem('theme', theme); }, [theme]); const toggleTheme = () => { setTheme(prev => prev === 'light' ? 'dark' : 'light'); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); } // 自定义Hook,方便其他组件快速获取主题 export function useTheme() { const context = useContext(ThemeContext); if (!context) { throw new Error('useTheme必须在ThemeProvider内部使用'); } return context; }
2. 在globals.css中定义主题样式
添加对应主题的body全局样式:
/* 浅色主题 */ .light-theme { background-color: #ffffff; color: #1a1a1a; } /* 深色主题 */ .dark-theme { background-color: #1a1a1a; color: #ffffff; } /* 保留原有全局样式 */
3. 在RootLayout中嵌套ThemeProvider
保持RootLayout为服务端组件(无需加'use client',确保metadata正常导出),将ThemeProvider嵌套在body内部包裹所有子组件:
import './globals.css'; import { Inter } from 'next/font/google'; import ThemeProvider from "./hooks/theme"; import CardsProvider from "./hooks/getCards"; const inter = Inter({ subsets: ['latin'] }); export const metadata = { title: 'Create Next App', description: 'Generated by create next app', }; export default function RootLayout({ children, }: { children: React.ReactNode }) { return ( <html lang="en"> <body className={inter.className}> <ThemeProvider> <CardsProvider> {children} </CardsProvider> </ThemeProvider> </body> </html> ); }
额外:在其他组件中切换主题
如果需要在页面/组件中实现主题切换,直接导入useTheme Hook即可:
'use client'; import { useTheme } from '../hooks/theme'; export default function ThemeToggle() { const { theme, toggleTheme } = useTheme(); return ( <button onClick={toggleTheme}> 切换到{theme === 'light' ? '深色' : '浅色'}主题 </button> ); }
内容的提问来源于stack exchange,提问作者Kolom
相关产品推荐
相关产品推荐

