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,提问作者أحمد المبيض
相关产品推荐
相关产品推荐

