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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 15:46:22