使用Redux-Toolkit做主题切换全局状态时Store reducer无效错误求解
问题根源
你抛出的reducer无效错误,核心是combineReducers传入的对象里,存在没有有效reducer的属性:
- 你配置的rootReducer里,
data字段对应的是combineReducers({}),空对象传入combineReducers会返回无效的reducer,触发报错。 - 另外themeSlice里定义了空的
extraReducers对象,新版Redux Toolkit已经不推荐使用对象写法写extraReducers,空对象也可能在部分版本触发校验问题。
修复方案
第一步 修正Store配置
要么给data字段加对应的reducer,要么暂时删掉空的data字段,修改后代码如下:
import { combineReducers, configureStore } from '@reduxjs/toolkit'; import { TypedUseSelectorHook, useSelector } from 'react-redux'; import { themeUiReducer } from './ui/themeSlice'; export const rootReducer = combineReducers({ // 如果暂时没有data相关的reducer,先删掉空的data配置,后续加了再放回来 ui: combineReducers({ theme: themeUiReducer }), }); export const store = configureStore({ reducer: rootReducer, // 你原来的middleware配置是冗余的,configureStore默认会返回getDefaultMiddleware的结果,不需要手动写 }); export type RootState = ReturnType<typeof store.getState> export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector;
第二步 修正themeSlice的空extraReducers
把空的extraReducers删掉即可,没有额外异步reducer需求不需要保留这个字段:
import { createSlice, PayloadAction } from "@reduxjs/toolkit"; export interface IThemes { themeMode: boolean | undefined, useOs: boolean | undefined } const initialState: IThemes = { themeMode: false, useOs: false } const themeSlice = createSlice({ name: 'themeUI', initialState: initialState, reducers: { setThemeMode: (state, action: PayloadAction<boolean | undefined>) => { state.themeMode = action.payload; }, setUseOs: (state, action: PayloadAction<boolean | undefined>) => { state.useOs = action.payload; }, } }) export const { setThemeMode, setUseOs } = themeSlice.actions; export const themeUiReducer = themeSlice.reducer;
第三步 优化App.tsx的状态读取(可选)
你现在还在使用本地的useState存主题状态,没有用到Redux的全局状态,修改成从全局store读取的话,后续在导航栏、页脚修改主题才能全局生效:
import React, { useCallback } from 'react'; import './App.css'; import Layout from './components/Layout'; import { Route, Switch } from 'react-router-dom'; import { ThemeProvider, createTheme, useMediaQuery, CssBaseline } from '@mui/material'; import ThemeSwitcher from './theme/themeSwitcher'; import { useDispatch } from 'react-redux'; import { setThemeMode, setUseOs } from './store/ui/themeSlice'; // 导入你封装的类型化useSelector import { useTypedSelector } from './store'; function App() { const dispatch = useDispatch(); // 从Redux全局状态读取主题配置 const { themeMode, useOs } = useTypedSelector(state => state.ui.theme); // Get OS-level preference for dark mode const prefersDarkMode: boolean | undefined = useMediaQuery("(prefers-color-scheme: dark)"); const activeDarkMode = useOs ? prefersDarkMode : themeMode; const themeOption = (b: boolean) => (b ? "dark" : "light"); const theme = React.useMemo( () => createTheme({ palette: { mode: themeOption(activeDarkMode!) } }), [activeDarkMode] ); const dispatchTheme = useCallback( (themeMode: boolean | undefined) => { if(themeMode === null) dispatch(setThemeMode(prefersDarkMode)); else dispatch(setThemeMode(themeMode)); }, [dispatch, prefersDarkMode] ); return ( <ThemeProvider theme={theme}> <CssBaseline /> <ThemeSwitcher useOs={false} themeChanger={dispatchTheme} /> <Layout> <Switch> <Route /> <Route /> <Route /> </Switch> </Layout> </ThemeProvider> ); } export default App;
内容的提问来源于stack exchange,提问作者Simon Mayrhofer
相关产品推荐
相关产品推荐

