我尝试使用
localStorage
设置 Redux Toolkit 切片的初始值,但是,对于 Next.js,服务器端未定义 window
,从而导致错误。
通常,解决方案是使用
if (typeof window !== "undefined")
,但对于initialState,它并不完全正确,因为即使未定义窗口,也必须始终给出一个值,并且您不知道该值是否正确。我知道存在惰性初始化器,但是我需要从页面加载过程开始就有一个初始值,因为我需要根据该值为根 HTML 元素设置一个 darkMode
属性。
这是我的暗模式切片代码:
"use client";
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
export interface DarkModeState {
value: boolean;
};
const initialState: DarkModeState = {
value: window.localStorage.getItem("darkMode")?.toString() === "true"; //! Server error
};
export const darkModeSlice = createSlice({
name: "darkMode",
initialState,
reducers: {
setDarkMode: (state, action: PayloadAction<boolean>) => {
state.value = action.payload;
},
},
});
export const { setDarkMode } = darkModeSlice.actions;
export default darkModeSlice.reducer;
这是我设置 darkMode 属性的根元素:
"use client";
import { useAppSelector } from "@/store/hooks";
import React, { ReactNode, useEffect } from "react";
export default function AppWrapper({ children }: { children: ReactNode }) {
const darkMode = useAppSelector((state) => state.darkMode.value);
useEffect(() => {}); // just to make sure it's a client component
return (
<div id="app" data-theme-app={darkMode ? "dark" : "light"}>
{children}
</div>
);
};
我已经尝试使用
useEffect
钩子将服务器的可能不正确的暗模式状态的随机初始值与客户端 localStorage
上的随机初始值进行匹配,但是,这会导致每次加载时出现丑陋的主题切换页面。
是否有一种正确的方法可以从加载过程开始就正确初始化暗模式状态,以便服务器和客户端之间不存在不匹配问题和水合作用问题?
尝试用
useLayoutEffect
代替 useEffect
。
useLayoutEffect 是 useEffect 的一个版本,在浏览器重新绘制屏幕之前触发。
useLayoutEffect(() => {
const isDarkMode = localStorage.getItem("darkMode");
// And Dispatch ...
}, []);