React Redux Persist 如何在REHYDRATE后正确派发action更新类名
问题原因
你在extraReducers里监听REHYDRATE拿到的state.isDarkMode一直是初始的false,根本原因是REHYDRATE动作触发的节点,redux-persist还没把本地存的持久化数据合并到当前slice的state上,你要的真实存在本地的值是挂在这个action的payload字段上的,按你自己配的slice结构取就行。
你之前写的根组件useEffect监听方案虽然能实现效果,但因为要等PersistGate加载完成、组件首次渲染后才会执行类名同步,很容易出现首屏主题闪烁的问题,体验不好。
无useEffect的最优实现
直接在REHYDRATE的处理回调中取出持久化的主题值,同步更新HTML根节点的class即可,这个执行时机比组件渲染早,能彻底避免首屏主题闪烁问题,也不需要在组件层写任何监听逻辑。
- 先抽离重复的类名操作逻辑,避免在reducer和action里写重复代码:
const syncDarkModeClass = (isDarkMode: boolean) => { isDarkMode ? document.documentElement.classList.add('dark') : document.documentElement.classList.remove('dark') }
- 修改slice代码,修正REHYDRATE的取值逻辑,复用抽离的同步方法:
import { createSlice } from '@reduxjs/toolkit' import { REHYDRATE } from "redux-persist"; const syncDarkModeClass = (isDarkMode: boolean) => { isDarkMode ? document.documentElement.classList.add('dark') : document.documentElement.classList.remove('dark') } export const settingsSlice = createSlice({ name: 'settings', initialState: { isDarkMode: false }, reducers: { toggleDarkMode: (state) => { state.isDarkMode = !state.isDarkMode // 手动切换主题时同步DOM类名 syncDarkModeClass(state.isDarkMode) }, }, extraReducers: (builder) => { builder.addCase(REHYDRATE, (state, action) => { // 从rehydrate动作的payload中取对应slice的持久化值 const persistedDarkMode = action.payload?.settings?.isDarkMode // 做类型校验,本地无缓存时payload可能为undefined,避免报错 if (typeof persistedDarkMode === 'boolean') { // 水合完成瞬间同步DOM类名 syncDarkModeClass(persistedDarkMode) } }); } }) export const { toggleDarkMode } = settingsSlice.actions export default settingsSlice.reducer
注意事项
- 取值时
action.payload.settings里的settings字段,要和你给当前slice设置的name值保持一致,后续如果改了slice的name,这里也要同步修改 - 必须加类型判断,应用首次启动本地没有持久化缓存时,
action.payload可能为undefined,不加判断容易抛错 - 这种实现方式把主题同步逻辑收敛在slice内部,不需要依赖组件生命周期,后续维护更方便
内容的提问来源于stack exchange,提问作者Samuele1818
相关产品推荐
相关产品推荐

