You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:55:20