MUI 5使用makeStyles报错:Cannot read properties of undefined (reading 'up')
问题
基于MUI 5构建的React应用原本运行正常,删除yarn.lock并重新安装所有包后,应用无法启动,出现大量MUI相关错误,已用ThemeProvider包裹路由但问题依旧。
错误信息
MUI: The
stylesargument provided is invalid.
You are providing a function without a theme in the context.Uncaught TypeError: Cannot read properties of undefined (reading 'up')
相关代码文件
package.json
{ "name": "doctor", "version": "0.1.0", "private": true, "dependencies": { "@date-io/date-fns": "^2.11.0", "@date-io/moment": "^2.11.0", "@emotion/react": "^11.7.1", "@emotion/styled": "^11.6.0", "@material-ui/core": "^4.12.4", "@mui/icons-material": "^5.2.5", "@mui/lab": "^5.0.0-alpha.62", "@mui/material": "^5.10.7", "@mui/styles": "^5.10.7", "@mui/system": "^5.10.6", "@reduxjs/toolkit": "^1.7.1", "@stripe/react-stripe-js": "^1.7.0", "@stripe/stripe-js": "^1.22.0", "@testing-library/jest-dom": "^5.14.1", "@testing-library/react": "^12.0.0", "@testing-library/user-event": "^13.2.1", "@types/react-qr-reader": "^2.1.4", "autoprefixer": "^10.4.0", "web-vitals": "^2.1.0", "yup": "^0.32.11" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": [ "react-app", "react-app/jest" ] }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] }, "devDependencies": { "@types/chart.js": "^2.9.35", "@types/file-saver": "^2.0.5", "@types/jest": "^27.0.1", "@types/node": "^16.7.13", "@types/react": "^17.0.20", "@types/react-dom": "^17.0.9", "@types/react-redux": "^7.1.21", "@types/react-router-dom": "^5.1.7", "@types/react-table": "^7.7.9", "@types/redux-logger": "^3.0.9", "chartjs-plugin-datalabels": "^2.0.0", "lodash": "^4.17.21" }, "resolutions": { "@types/react": "17.0.2", "@types/react-dom": "17.0.2" } }
app.tsx
import React from "react"; import { ThemeProvider } from "@material-ui/core/styles"; import "./App.css"; import { loadStripe } from "@stripe/stripe-js"; import { Elements } from "@stripe/react-stripe-js"; // redux import { Provider } from "react-redux"; import store, { persistor } from "./redux/store"; import { PersistGate } from "redux-persist/integration/react"; // Routes import { ConnectedRouter } from "connected-react-router"; import history from "./redux/history"; import Router from "./Router"; // Theme import { SnackbarProvider } from "notistack"; import CustomSnackbarProvider from "./providers/CustomSnackbarProvider"; import muiTheme from "./constants/theme"; import { makeStyles } from "@mui/styles"; import { Chart as ChartJS, CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, Filler, BarElement, } from "chart.js"; import ChartDataLabels from "chartjs-plugin-datalabels"; import { useLocation } from "react-router-dom"; import AccessControlWrapper from "./components/AccessControlWrapper"; ChartJS.register( CategoryScale, LinearScale, PointElement, LineElement, Title, Tooltip, Legend, BarElement, ChartDataLabels, Filler ); declare var process: { env: { REACT_APP_STRIPE_PUBLISH_KEY: string; REACT_APP_STRIPE_CLIENT_SECRET: string; }; }; const stripePromise = loadStripe(process.env.REACT_APP_STRIPE_PUBLISH_KEY); function App() { return ( <Provider store={store}> <PersistGate persistor={persistor} loading={null}> <Elements stripe={stripePromise}> <ConnectedRouter history={history}> <ThemeProvider theme={muiTheme}> <SnackbarProvider maxSnack={3} anchorOrigin={{ vertical: "top", horizontal: "center", }} > <CustomSnackbarProvider> <AccessControlWrapper> <Router /> </AccessControlWrapper> </CustomSnackbarProvider> </SnackbarProvider> </ThemeProvider> </ConnectedRouter> </Elements> </PersistGate> </Provider> ); } export default App;
主题配置文件
import { createTheme } from "@material-ui/core/styles"; declare module "@mui/material/styles" { interface BreakpointOverrides { xs: true; // removes the `xs` breakpoint sm: true; md: true; lg: true; xl: true; xxl: true; } } const theme = { palette: { background: { default: "#FBFBFB", }, primary: { light: "#9D9DA5", main: "#2EC2A5", contrastText: "#fff", }, secondary: { light: "#ff7961", main: "#24344B", dark: "#ba000d", contrastText: "#000", }, }, }; const BREAKPOINTS = { xs: 0, sm: 600, md: 900, lg: 1200, xl: 1536, xxl: 1800, }; let muiTheme = createTheme({ breakpoints: { values: BREAKPOINTS, }, ...theme, }); export default muiTheme;
Styles.tsx
import { Theme } from "@mui/system"; import { makeStyles } from "@mui/styles"; export const useStyles = makeStyles((theme: Theme) => ({ intro: { padding: "0 2rem", [theme.breakpoints.up("lg")]: { padding: "0 4rem !important", }, }, }));
解决方法
核心原因
项目同时混用了MUI 4(@material-ui/core)和MUI 5(@mui/*)的依赖包,且ThemeProvider和createTheme使用了MUI 4的版本,而makeStyles用的是MUI 5的@mui/styles,两者上下文不兼容,导致makeStyles无法获取到主题对象,最终抛出错误。
具体修复步骤
移除MUI 4依赖
编辑package.json,删除"@material-ui/core": "^4.12.4"这一行,彻底移除MUI 4的核心包。替换ThemeProvider导入
修改app.tsx中的ThemeProvider导入路径,从MUI 4切换到MUI 5:// 替换前 import { ThemeProvider } from "@material-ui/core/styles"; // 替换后 import { ThemeProvider } from "@mui/material/styles";替换createTheme导入
修改主题配置文件中的createTheme导入路径,同样切换到MUI 5:// 替换前 import { createTheme } from "@material-ui/core/styles"; // 替换后 import { createTheme } from "@mui/material/styles";重新安装依赖
删除node_modules文件夹和yarn.lock文件,然后执行:yarn install验证修复
启动应用:yarn start
完成以上步骤后,makeStyles就能正确从MUI 5的ThemeProvider获取主题上下文,错误会被解决。
内容的提问来源于stack exchange,提问作者callmeizaz

