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

Next.js 13深色模式切换异常:切换函数触发两次致值恢复默认

Next.js 13 深色模式主题切换问题解决方案

问题说明

在Next.js 13中实现CSS深色模式切换时,切换函数会触发两次,导致主题值自动恢复为默认的light模式。普通React项目里移除StrictMode就能解决,但在Next.js中设置reactStrictMode: false没有效果。

问题原因

你的toggle函数依赖闭包捕获的旧theme值,在StrictMode(即使Next.js配置关闭后仍可能存在部分严格模式行为)下,组件会被重复挂载,导致toggle使用的是初始状态的theme值,切换操作后又被重置回去。

修复方案

核心修复:使用函数式更新setTheme

修改主题上下文里的toggle函数,改用setTheme的函数式更新形式,每次切换都会获取最新的主题状态,不再依赖闭包中的旧值:

"use client";

import { createContext, useEffect, useState } from "react";

export const ThemeContext = createContext();

const getFromLocalStorage = () => {
  if (typeof window !== "undefined") {
    const value = localStorage.getItem("theme");
    return value || "light";
  }
};

export const ThemeContextProvider = ({ children }) => {
  const [theme, setTheme] = useState(() => getFromLocalStorage());

  // 改用函数式更新,确保拿到最新的theme状态
  const toggle = () => {
    setTheme(prevTheme => prevTheme === "light" ? "dark" : "light");
  };

  useEffect(() => {
    localStorage.setItem("theme", theme);
    // 同步更新DOM类名,确保主题样式生效
    document.documentElement.classList.toggle("dark", theme === "dark");
  }, [theme]);

  // 初始化时同步本地存储的主题到DOM
  useEffect(() => {
    const savedTheme = getFromLocalStorage();
    setTheme(savedTheme);
    document.documentElement.classList.toggle("dark", savedTheme === "dark");
  }, []);

  return (
    <ThemeContext.Provider value={{ theme, toggle }}>
      {children}
    </ThemeContext.Provider>
  );
};

辅助:确保StrictMode完全关闭

如果上述修复后仍有问题,检查根布局文件(app/layout.js),确保没有手动包裹StrictMode:

// app/layout.js
import { ThemeContextProvider } from "@/context/ThemeContext";

export default function RootLayout({ children }) {
  return (
    <html lang="zh-CN">
      <body>
        <ThemeContextProvider>
          {children}
        </ThemeContextProvider>
      </body>
    </html>
  );
}

切换组件优化(可选)

将组件中的class改为React规范的className,避免控制台警告:

"use client";
import { ThemeContext } from "@/context/ThemeContext";
import React, { useContext } from "react";
import "./themeToggle.css";

const ThemeToggle = () => {
  const {toggle} = useContext(ThemeContext)
  return (
    <div>
      <label onClick={toggle} className="switch">
        <input type="checkbox" />
        <span className="slider"></span>
      </label>
    </div>
  );
};

export default ThemeToggle;

内容的提问来源于stack exchange,提问作者أحمد المبيض

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 11:12:03