React中Redux存储的MUI ThemeProvider主题应用异常求助
MUI主题切换Redux集成问题排查
我在项目中尝试用Redux存储MUI主题,实现明暗模式切换功能,默认是浅色模式(逻辑在toggle-theme.ts reducer里)。已经确认主题对象能正常返回,PageShell里的切换按钮也能触发主题切换动作。
按照Material 5.0文档要求,我在index.tsx里用@mui/material/styles的ThemeProvider包裹了整个项目,还拆分了Root组件来在组件内使用useSelector。但现在PageShell组件里出现大量未定义错误,比如Cannot read properties of undefined (reading 'h1') in Typography component,涉及排版变体、pxToRem属性等。虽然浏览器控制台里theme是合法对象,但主题根本没加载生效。
相关代码
index.tsx
import React from 'react'; import { createRoot } from 'react-dom/client'; import { BrowserRouter, Route, Routes } from 'react-router-dom'; import { ThemeProvider } from '@mui/material/styles'; import { Provider, useSelector } from 'react-redux'; /* Custom variables */ import { store } from './store/store'; /* Pages import */ const Root = () => { const theme = useSelector((state: any) => state.theme); console.log(theme); return ( <ThemeProvider theme={theme}> <BrowserRouter> <Routes> <Route path="/" element={<PageShell />}> <Route index element={<Home />} /> <Route path="about" element={<About />} /> <Route path="contact" element={<Contact />} /> <Route path="/content" element={<ContentShell />}> <Route path="blog" element={<Blog />} /> <Route path="events" element={<Events />} /> <Route path="github" element={<GitHub />} /> <Route path="music" element={<Music />} /> <Route path="portfolio" element={<Portfolio />} /> <Route path="writing" element={<Writing />} /> </Route> <Route path="*" element={<NotFound />} /> </Route> </Routes> </BrowserRouter> </ThemeProvider> ) }; const container: any = document.getElementById('root'); const root = createRoot(container); root.render( <React.StrictMode> <Provider store={store}> <Root /> </Provider> </React.StrictMode> );
actions.ts
export const TOGGLE_THEME = 'TOGGLE_THEME'; export function toggleTheme() { return { type: TOGGLE_THEME }; }
toggle-theme.ts (reducer)
import { green, red, yellow } from '@mui/material/colors'; import { createTheme } from '@mui/material/styles'; import { TOGGLE_THEME } from '../actions/actions'; // Custom Theme creation export const lightTheme = createTheme({ // LONG object }); export const darkTheme = createTheme({ palette: { primary: { main: '#a4c3e6', contrastText: '#fff' }, secondary: { main: '#333', contrastText: '#dedede' }, // LONG object #2 }); const initialState = { theme: lightTheme }; export function themeReducer(state = initialState, action: any) { switch (action.type) { case TOGGLE_THEME: return { ...state, theme: state.theme === lightTheme ? darkTheme : lightTheme }; default: return state; } }
page-shell.tsx
import { useDispatch, useSelector } from 'react-redux'; import { IconButton, Typography } from '@mui/material'; import { DarkMode, LightMode } from '@mui/icons-material'; import { toggleTheme } from '../../store/actions/actions'; export const PageShell = () => { const darkMode = useSelector((state: any) => state.theme.darkMode); const dispatch = useDispatch(); return ( <> <IconButton color="secondary" disableRipple disableFocusRipple onClick={() => dispatch(toggleTheme())}> {darkMode ? <DarkMode /> : <LightMode />} </IconButton> <Typography variant="h1">PageShell page</Typography> </> ) }; export default PageShell;
解决思路
1. 修正Redux State取值路径
你的reducer初始状态是{ theme: lightTheme },所以Root组件里useSelector((state: any) => state.theme)拿到的是包含theme属性的对象,而非实际的MUI主题实例。正确取值应该是:
const theme = useSelector((state: any) => state.theme.theme);
2. 修复PageShell的明暗模式判断逻辑
你的主题对象里没有darkMode属性,应该通过主题的palette.mode或者自定义标识来判断:
// 方式1:用palette.mode(创建主题时要指定mode) const currentTheme = useSelector((state: any) => state.theme.theme); const isDarkMode = currentTheme.palette.mode === 'dark'; // 方式2:对比主题引用 import { lightTheme } from '../../store/reducers/toggle-theme'; const currentTheme = useSelector((state: any) => state.theme.theme); const isDarkMode = currentTheme !== lightTheme;
3. 提升主题对比的可靠性
直接对比对象引用可能在热重载等场景下失效,建议在主题里添加自定义标识:
// 创建主题时添加标识 export const lightTheme = createTheme({ palette: { mode: 'light' }, custom: { isLight: true } }); export const darkTheme = createTheme({ palette: { mode: 'dark' }, custom: { isLight: false } }); // reducer里切换逻辑改为 case TOGGLE_THEME: return { ...state, theme: state.theme.custom.isLight ? darkTheme : lightTheme };
4. 增加TypeScript类型校验
给theme添加类型约束,避免隐式any的问题:
import { Theme } from '@mui/material/styles'; const theme = useSelector((state: any) => state.theme.theme) as Theme;
内容的提问来源于stack exchange,提问作者Terence Waters
相关产品推荐
相关产品推荐

