Next.js14中'use client'组件仍报localStorage未定义的原因咨询
Next.js 14中ThemeContext使用localStorage报错的原因解析
我在为Next.js 14网站配置Tailwind全局主题时,创建了ThemeContext并在文件顶部添加了'use client'指令,但组件仍在服务端渲染阶段抛出错误:
⨯ src\contexts\ThemeContext.tsx (22:28) @ localStorage ⨯ ReferenceError: localStorage is not defined at eval (./src/contexts/ThemeContext.tsx:20:29) at ThemeProvider (./src/contexts/ThemeContext.tsx:19:78) 20 | export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => { 21 | const [theme, setTheme] = useState<Theme>(() => { > 22 | const storedTheme = localStorage?.getItem('theme'); | ^ 23 | return storedTheme === Theme.DARK ? Theme.DARK : Theme.LIGHT; 24 | }); 25 | ✓ Comp
我通过添加mounted状态解决了该问题,代码如下:
'use client'; ... export const ThemeProvider: React.FC<ThemeProviderProps> = ({ children }) => { const [theme, setTheme] = useState<Theme>(Theme.LIGHT); const [mounted, setMounted] = useState(false); useEffect(() => { if (mounted) { return } const storedTheme = localStorage?.getItem('theme'); console.log("storedTheme", storedTheme) setTheme(storedTheme === Theme.DARK ? Theme.DARK : Theme.LIGHT); setMounted(true) // eslint-disable-next-line react-hooks/exhaustive-deps }, []); useEffect(() => { if (!mounted) { return } if (theme === Theme.DARK) { document.documentElement.classList.add('dark'); localStorage?.setItem('theme', Theme.DARK); } else { document.documentElement.classList.remove('dark'); localStorage?.setItem('theme', Theme.LIGHT); } }, [theme, mounted])
问题产生的核心原因
'use client'不代表完全跳过服务端渲染:Next.js 14的App Router中,客户端组件依然会在服务端完成初始渲染(SSR),组件的初始化代码(包括useState的初始值回调函数)会先在服务端环境执行一遍。localStorage是浏览器专属API:服务端运行在Node.js环境中,不存在localStorage这个全局对象,所以服务端执行到读取localStorage的代码时,直接抛出未定义错误。useState初始值回调的执行时机:你把读取localStorage的逻辑放在了useState的初始值回调里,这个回调会在组件初始化阶段(服务端+客户端)都执行,这就触发了服务端的错误。
你的解决方案为什么有效
你通过mounted状态配合useEffect,把依赖浏览器API的逻辑延迟到客户端挂载完成后执行:
- 空依赖的
useEffect只会在组件客户端挂载完成后执行一次,此时已经处于浏览器环境,localStorage可以正常访问。 - 先给
theme设置默认的Theme.LIGHT,服务端渲染时用这个默认值,避免了服务端访问localStorage的问题;客户端挂载后再读取本地存储的主题并更新状态,同时标记mounted为true,后续的主题变更逻辑也只在mounted为true时执行,确保所有浏览器API操作都只在客户端运行。
内容的提问来源于stack exchange,提问作者Matheus Felipe Vieira Santiago
相关产品推荐
相关产品推荐

