Next.js+MUI SSR场景下Switch异常及className不匹配问题
问题根因
你遇到的报错和组件渲染异常属于同源问题,核心原因是服务端渲染与客户端渲染的执行环境不一致:
js-cookie只能在浏览器客户端环境运行,服务端渲染阶段无法读取浏览器存储的Cookie,原有逻辑在Store初始化时直接调用Cookies.get(),服务端执行时永远拿到空值,默认渲染浅色主题、Switch关闭状态的DOM- 客户端注水阶段才会真正读取到Cookie里存储的深色模式配置,触发二次渲染输出深色主题、Switch开启的DOM,前后端输出的DOM结构、元素className完全不一致,就会抛出
Prop className did not match Server and Client报错,同时出现Switch首屏显示关闭、二次渲染后才恢复正常的异常 - 原有
_document.js仅处理了MUI基础样式的服务端收集,没有处理主题状态在服务端/客户端的同步逻辑,所以参考的通用集成方案没有生效。
修复步骤
1. 调整全局状态初始化逻辑,避免SSR阶段直接读取客户端Cookie
修改Store.js,将darkMode初始值统一设为默认值,不在服务端执行阶段读取Cookie:
import Cookies from "js-cookie"; import { createContext, useReducer } from "react"; export const Store = createContext(); // SSR阶段统一使用默认浅色主题,不读取客户端存储 const initialState = { darkMode: false, }; const reducer = (state, action) => { switch (action.type) { case "DARK_MODE_ON": return { ...state, darkMode: true }; case "DARK_MODE_OFF": return { ...state, darkMode: false }; default: return state; } }; export const StoreProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); const value = { state, dispatch }; return <Store.Provider value={value}>{children}</Store.Provider>; };
2. 修改_app.js,在客户端注水完成后同步Cookie中的主题配置
通过useEffect钩子在客户端首屏渲染完成后读取Cookie同步主题状态,同时保留原有清理服务端注入JSS样式的逻辑,保证前后端首屏输出的DOM完全一致:
import { useEffect, useContext } from "react"; import "../styles/globals.css"; import { StoreProvider, Store } from "../utils/Store"; import Cookies from "js-cookie"; // 抽离内部组件用于访问全局Context const AppContent = ({ Component, pageProps }) => { const { dispatch } = useContext(Store); useEffect(() => { // 清理服务端注入的JSS样式 const jssStyles = document.querySelector("#jss-server-side"); if (jssStyles) { jssStyles.parentElement.removeChild(jssStyles); } // 客户端环境就绪后读取Cookie同步主题状态 const darkModeCookie = Cookies.get("darkMode"); dispatch({ type: darkModeCookie === "ON" ? "DARK_MODE_ON" : "DARK_MODE_OFF" }); }, [dispatch]); return <Component {...pageProps} /> } function MyApp({ Component, pageProps }) { return ( <StoreProvider> <AppContent Component={Component} pageProps={pageProps} /> </StoreProvider> ); } export default MyApp;
useEffect内的逻辑只会在浏览器端执行,不会在服务端渲染阶段运行,从根源避免前后端渲染结果不一致的问题。
3. 验证修复效果
重启开发服务后校验:
- 控制台不再出现
Prop className did not match Server and Client报错 - 刷新页面时Switch开关状态与Cookie存储的主题配置完全一致,不会出现先关闭再自动开启的首屏异常
- 深色/浅色主题切换功能正常,手动刷新页面后主题状态可正常保留
内容的提问来源于stack exchange,提问作者Mahesh Yadav
相关产品推荐
相关产品推荐

