I'm studying MUI, and in the course, the instructor asks me to style just one component and not the entire theme.
For that, it uses the makeStyles function and spreads the theme.mixins.toolbar. The problem is when I do this, I have the following error:
TypeError: Cannot read properties of undefined (reading 'toolbar')
This course is apparently in version 4, and I am using version 5. Despite this, my code appears to follow the changes that the documentations asks for. So what could be causing this error?
app.js
import "./App.css";
import Header from "./components/ui/Header";
import { ThemeProvider } from "#material-ui/core/styles";
import theme from "./components/ui/Theme";
function App() {
return (
<ThemeProvider theme={theme}>
<Header />
</ThemeProvider>
);
}
export default App;
Theme.js
import { createTheme } from "#material-ui/core/styles";
const arcBlue = "#0B72B9";
const arcOrange = "#FFBA60";
export default createTheme({
typography: {
h3: {
fontWeight: 100,
},
},
palette: {
common: {
blue: `${arcBlue}`,
orange: `${arcOrange}`,
},
primary: {
main: `${arcBlue}`,
},
secondary: {
main: `${arcOrange}`,
},
},
});
header/index.jsx
import React from "react";
import AppBar from "#mui/material/AppBar";
import Toolbar from "#mui/material/Toolbar";
import useScrollTrigger from "#mui/material/useScrollTrigger";
import Typography from "#mui/material/Typography";
import { makeStyles } from "#material-ui/styles";
function ElevationScroll(props) {
const { children, window } = props;
const trigger = useScrollTrigger({
disableHysteresis: true,
threshold: 0,
target: window ? window() : undefined,
});
return React.cloneElement(children, {
elevation: trigger ? 10 : 0,
});
}
const useStyles = makeStyles((theme) => ({
toolbarMargin: { ...theme.mixins.toolbar },
}));
function Header() {
const classes = useStyles();
return (
<React.Fragment>
<ElevationScroll>
<AppBar color="primary">
<Toolbar>
<Typography variant="h3" component="h3">
Nome de teste
</Typography>
</Toolbar>
</AppBar>
</ElevationScroll>
<div className={classes.toolBarMargin} />
</React.Fragment>
);
}
export default Header;
Since you're using v5, change your ThemeProvider, createTheme and makeStyles import path from:
import { ThemeProvider, createTheme, makeStyles } from "#material-ui/core/styles";
To:
import { ThemeProvider, createTheme } from "#mui/material/styles";
import { makeStyles } from "#mui/styles";
#material-ui/core is v4 package and #mui/material is the v5 equivalent. The API from the 2 versions are not compatible. In v5, makeStyles is also moved to a legacy package called #mui/styles, if you are using MUI v5 in a new project, you should switch completely to styled/sx API as recommended by the MUI team.
Related answers
Difference between #mui/material/styles and #mui/styles?
Cannot use palette colors from MUI theme
MUI createTheme is not properly passing theme to MUI components
I created a project on CodeSandbox and it doesn't seem the problem in code.
I guess you need to check the version of package you installed in package.json file.
Here is the link to the CodeSandbox project and you can see the console.log message on console tab.
https://codesandbox.io/s/check-makestyle-eq67m?file=/src/components/ui/Header/index.js
I faced similar issue with Can not read properties of undefined, reading refs thrown by makeStyles. In my case it was mui v5 and the issue occurred after upgrading to React 18.
In my case it turned out I had some legacy makeStyles(()=>createStyles) wrapped in a manner:
const useStyles = (minWidth: number) =>
makeStyles((theme: Theme) => ...
I had to change it to:
const useStyles =
makeStyles((theme: Theme) => ...
Perhaps it will be useful for somebody else facing the issue.
Solution
Install #mui/styles
npm install #mui/styles
Change
import { ThemeProvider, createTheme } from "#material-ui/core/styles";
To
import { ThemeProvider, createTheme } from "#mui/material/styles";
And
import { makeStyles } from "#material-ui/styles";
To
import { makeStyles } from "#mui/styles";
And
<div className={classes.toolBarMargin} />
To
<div className={classes.toolbarMargin} />
Related
I have an older external component library which is still in the process of being migrated from MUI v4 to v5.
One of these components which is still using v4 needs to be used in a brand new codebase, which is set up for v5.
The problem is, this external component completely loses all of its styles when I attempt to use it in the newer codebase.
I would like to avoid having to install and set up MUI v4 o this new codebase. Did I miss something in the setup to be able to use components that are still on MUI v4?
Here is a rough sketch of my setup:
Older component from an external library
import { Grid, Button, makeStyles } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
gridRoot: {
// a bunch of styles
},
buttonRoot: {
// more styles
}
}))
const SomeOldComponent = () => {
const classes = useStyles()
return (
<Grid classes={{ root: classes.gridRoot }}>
I am an old component from an external library. I still use MUI v4.
<Button classes={{ root: classes.buttonRoot }}>Some button</Button>
</Grid>
)
}
export default SomeOldComponent
New codebase
import React from "react"
import { ThemeProvider } from "#mui/material/styles"
import { StylesProvider, createGenerateClassName } from '#mui/styles'
import { createTheme } from "./myTheme"
const classNameOpts = {
productionPrefix: '',
disableGlobal: true,
seed: 'mySeed',
}
const generateClassName = createGenerateClassName(classNameOpts)
export const ApplicationWrapper = ({ children }) => (
<StylesProvider generateClassName={generateClassName}>
<ThemeProvider theme={createTheme()}>{children}</ThemeProvider>
</StylesProvider>
)
export default ApplicationWrapper
Usage of the older component in the new codebase
The issue I have here is that in SomeOldComponent, none of its styles set in useStyles are being applied.
import { Grid, Button } from '#mui/material'
import SomeOldComponent from 'external-library'
const MyComponent = () => {
return (
<Grid>
I am a new component in a new MUI v5 only codebase
<SomeOldComponent/>
<Button>Do something!</Button>
</Grid>
)
}
export default MyComponent
I'm trying to toggle between light and dark theme in react using a Switch from material-ui. It is happening only once in which it is toggling from dark to light theme. This is the link to the code sandbox where all the code is hosted.
With the help of console logs I can see the theme changing in console but react is not updating it.
I know react state updates are asynchronous but what is the work around I can do for this?
Here is a working code:
import React, { useState } from "react";
import { ThemeProvider, CssBaseline } from "#material-ui/core";
import darkTheme from "./Theme/darkTheme";
import lightTheme from "./Theme/lightTheme";
import PrimarySearchAppBar from "./Components/Appbar";
import { createMuiTheme } from "#material-ui/core/styles";
function App() {
const [dark, setDark] = useState(true);
const Theme = dark ? createMuiTheme(darkTheme) : createMuiTheme(lightTheme);
return (
<ThemeProvider theme={Theme}>
<CssBaseline />
<div>
<PrimarySearchAppBar thm={dark} Togglethm={setDark} />
</div>
</ThemeProvider>
);
}
export default App;
Please note the two lines that are different from your code:
import { createMuiTheme } from "#material-ui/core/styles";
...
...
const Theme = dark ? createMuiTheme(darkTheme) : createMuiTheme(lightTheme);
It appears that for the themes to work MUI needs to create the theme (using createMuiTheme on every render). I do realize you're using createMuiTheme in your theme definitions (darkTheme.js and lightTheme.js), but those are superfluous. You can safely rewrite those files like so (which is what I did in my sandbox):
const darkTheme = {
palette: {
type: "dark",
primary: {
main: "#00e676"
},
secondary: {
main: "#e6006f"
}
}
};
export default darkTheme;
Sandbox: https://codesandbox.io/s/delicate-butterfly-d8jqd?file=/src/App.js
All you have to do is to import and use createMuiTheme instead of using the theme directly and you can export the plain objects from dakTheme.js and lightTheme.js
you have to recreate theme each time the app stat changes (user toggles theme)
to avoid unnecessary reRenders of the ThemeProvider and as a result reRendering of the whole app you should use a useMemo react hook for changing the theme depending on the dark state.
App.js
import React, { useState, useMemo } from "react";
import { ThemeProvider, CssBaseline } from "#material-ui/core";
import darkTheme from "./Theme/darkTheme";
import lightTheme from "./Theme/lightTheme";
import PrimarySearchAppBar from "./Components/Appbar";
import { createMuiTheme } from "#material-ui/core";
function App() {
const [dark, setDark] = useState(true);
const Theme = useMemo(() => createMuiTheme(dark ? darkTheme : lightTheme), [dark]);
return (
<ThemeProvider theme={Theme}>
<CssBaseline />
<div>
<PrimarySearchAppBar thm={dark} Togglethm={setDark} />
</div>
</ThemeProvider>
);
}
export default App;
dartkTheme.js
const darkTheme = {
palette: {
type: "dark",
primary: {
main: "#00e676",
},
secondary: {
main: "#e6006f",
}
},
}
export default darkTheme;
lightTheme.js
const lightTheme = {
palette: {
type: "light",
primary: {
main: "#2196f3"
},
secondary: {
main: "#f50057"
}
}
};
export default lightTheme;
So react-admin seems to have a feature where if you're idle for a little while and come back it will reload the data, presumably to make sure you're looking at the most up to date version of a record.
This is causing some issues for my editing feature that has some custom components. Is there a way to disable this auto-reload feature?
The auto-refresh is triggered by the loading indicator (the spinner icon that you see in the top right part of the app bar).
You can disable the auto-refresh by replacing the loading indicator by your own.
import * as React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { useSelector } from 'react-redux';
import { makeStyles } from '#material-ui/core/styles';
import CircularProgress from '#material-ui/core/CircularProgress';
import { useRefreshWhenVisible, RefreshIconButton } from 'react-admin';
const useStyles = makeStyles(
{
loader: {
margin: 14,
},
loadedIcon: {},
},
{ name: 'RaLoadingIndicator' }
);
const LoadingIndicator = props => {
const { classes: classesOverride, className, ...rest } = props;
useRefreshWhenVisible(); // <= comment this line to disable auto-refresh
const loading = useSelector(state => state.admin.loading > 0);
const classes = useStyles(props);
return loading ? (
<CircularProgress
className={classNames('app-loader', classes.loader, className)}
color="inherit"
size={18}
thickness={5}
{...rest}
/>
) : (
<RefreshIconButton className={classes.loadedIcon} />
);
};
LoadingIndicator.propTypes = {
classes: PropTypes.object,
className: PropTypes.string,
width: PropTypes.string,
};
export default LoadingIndicator;
You'll also need to put this button in a custom AppBar, inject your AppBar in a custom Layout, and use that Layout in your Admin, as explained in the react-admin Theming documentation.
I was able to turn off auto-refresh by dispatching the setAutomaticRefresh action from react-admin:
import { setAutomaticRefresh } from 'react-admin';
import { useDispatch } from 'react-redux';
// inside component
const dispatch = useDispatch();
dispatch(setAutomaticRefresh(false))
This action was added here as part of release 3.8.2.
I'm trying to include some components from #material-ui package at my app, using the ThemeProvider at the root, but i'm having some troubles.
Other components that use styles defined locally (without theme properties) render ok.
sandbox:
https://codesandbox.io/s/theme-provider-issues-t72f3
Here is how the theme is being created:
import React from "react";
import { createMuiTheme } from "#material-ui/core/styles";
import { ThemeProvider } from "#material-ui/styles";
import { blue, white } from "#material-ui/core/colors";
function AppTheme(props) {
const theme = createMuiTheme({
palette: {
primary: blue,
text: white
}
});
return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
}
export default AppTheme;
I'm not certain what you were trying to achieve with text: white, but that created an invalid structure for your theme. theme.palette.text should be an object rather than a color, and the error was caused by Material-UI looking for theme.palette.text.primary.
Changing AppTheme.js to the following solves the problem:
import React from "react";
import { createMuiTheme } from "#material-ui/core/styles";
import { ThemeProvider } from "#material-ui/styles";
import { blue } from "#material-ui/core/colors";
const theme = createMuiTheme({
palette: {
primary: blue
}
});
function AppTheme(props) {
return <ThemeProvider theme={theme}>{props.children}</ThemeProvider>;
}
export default AppTheme;
I've got another error, was fixed here
I am using Material-UI for a React project. However, I'm not sure as to how to apply a theme globally.
Here I have tried for individual components
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import CardCommon from '../../common/card/CardCommon';
import purple from '#material-ui/core/colors/purple';
import Button from '#material-ui/core/Button';
import { Link } from 'react-router-dom';
//const primary = red[500]; // #F44336
import { Paths } from '../../../Routes';
const theme = createMuiTheme({
palette: {
primary: { main: purple[500] }, // Purple and green play nicely together.
secondary: { main: '#11cb5f' }, // This is just green.A700 as hex.
},
});
So how can I change the primary and secondary colors globally?
You can structure your app like this. Wrap the child components inside a MuiThemeProvider and pass a createMuiTheme object to it as a theme property value.
Also typography: {useNextVariants: true }
fixes the following error:
(Warning: Material-UI: you are using the deprecated typography variants that will be removed in the next major release.)
The official Material UI docs have more detailed information on this:
MuiThemeProvider
Color
Edit the index.js file as follows
import React from 'react';
import ReactDOM from 'react-dom';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import './index.css';
import App from './App';
const theme = createMuiTheme({
palette: {
primary: {
light: '#fff',
main: 'rgb(23, 105, 170)',
dark: '#000'
},
secondary: {
main: '#f44336',
},
},
typography: {
useNextVariants: true
}
});
ReactDOM.render(
<MuiThemeProvider theme = { theme }>
<App />
</MuiThemeProvider>,
document.getElementById('root')
);
Material UI Version 5 code:
import { ThemeProvider, createTheme } from '#mui/material/styles';
const themeOptions = {
palette: {
type: 'light',
primary: {
main: '#3f51b5',
},
secondary: {
main: '#f50057',
},
},
};
const theme = createTheme(themeOptions);
<ThemeProvider theme={theme}>
<App />
</ThemeProvider>
In Material UI Version 5.4, the imports should be from from '#mui/material/styles';
import { ThemeProvider, createTheme } from '#mui/material/styles';
Doc: https://mui.com/customization/theming/#api