MUI5 :Cannot read properties of undefined (reading 'primary') - reactjs

I am using MUI5 with reactjs and I wanted to use the makeStyles to add some custom CSS to the MUI components but its not allowing me to use the default theme inside the makestyles so my code is :
import { Grid, Card, CardContent,Typography, CardActions,Button, AppBar, Toolbar,
Box,IconButton, Menu, MenuItem, InputBase, alpha, styled, Badge, Container, CardHeader,
CardMedia } from '#mui/material';
import EmailIcon from '#mui/icons-material/Email';
import NotificationsIcon from '#mui/icons-material/Notifications';
import React from 'react'
import { makeStyles } from '#mui/styles'
const useStyles = makeStyles((theme) => ({
root: ({
backgroundColor: theme.palette.primary.light,
color: theme.color,
}),
}));
const App = () => {
const classes = useStyles();
return(
<Fragment>
<Container className={classes.root}>
<Typography>
this is a very random text to fill
</Typography>
</Container>
</Fragment>
)
}
export default App;
its not allowing me to get to the theme props inside the makestyles

First of all, try assigning "root" as a normal object property like this-
const useStyles = makeStyles((theme) => ({
root: {
backgroundColor: theme.palette.primary.light,
color: theme.color,
}
}));
If it does not work, you may want to take a look at this-
https://mui.com/styles/basics/#using-the-theme-context

Related

Cannot find name 'classes'. TS2304?

I want to customize my appbar size on react Mui(currently using v4, but I get an error everytime I try to create a style.
My current Code
import React from "react";
import { AppBar, Toolbar, Typography, Box, makeStyles } from '#material-ui/core';
import { Link } from "react-router-dom";
import './Navbar.css'
const styles ={
customizeToolbar: {
minHeight: 36
}
};
function Navbar(){
return(
<AppBar position='sticky' >
<Toolbar variant='dense' style={classes.customizeToolbar} >
</Toolbar>
</AppBar>
)
}
export default Navbar;
import React from "react";
import { AppBar, Toolbar, Typography, Box, makeStyles } from '#material-ui/core';
import { Link } from "react-router-dom";
import './Navbar.css'
const styles ={
customizeToolbar: {
minHeight: 36
}
};
function Navbar(){
return(
<AppBar position='sticky' >
<Toolbar variant='dense' style={styles.customizeToolbar} >
</Toolbar>
</AppBar>
)
}
export default Navbar;
If you wish to customize the theme, you need to use the ThemeProvider component in order to inject a theme into your application. You must create styles with makeStyles, then you can create variable classes and use it in jsx with className not style. If you want customize not only Navbar but all components you can set ThemeProvider in index.js
import { createTheme, ThemeProvider } from '#mui/material/styles';
import { createStyles, makeStyles } from '#mui/styles';
const useStyles = makeStyles(() =>
createStyles({
customizeToolbar: {
minHeight: 36
}
})
);
const theme = createTheme();
function Navbar(){
const classes = useStyles();
return(
<ThemeProvider theme={theme}>
<AppBar position='sticky' >
<Toolbar variant='dense' className={classes.customizeToolbar} >
</Toolbar>
</AppBar>
</ThemeProvider>
)
}

Theme not being provided to sub-components using makeStyles MUI v5 [duplicate]

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} />

How to access material theme in shared component in react?

I have two react projects Parent and Common project (contains common component like header, footer)
I have material theme defined in Parent and configured in standard way using MuiThemeProvider.
However, this theme object is available inside components defined in Parent, but not in share project common.
Suggestions are appreciated.
Added below more details on Oct 30, 2020
Parent Component
import React from "react";
import "./App.css";
import { BrowserRouter, Switch, Route } from "react-router-dom";
import themeDefault from "./CustomTheme.js";
import { MuiThemeProvider } from "#material-ui/core/styles";
import { createMuiTheme } from "#material-ui/core/styles";
import Dashboard from "./containers/Dashboard/Dashboard";
import { Footer, Header } from "my-common-react-project";
function App() {
const routes = () => {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Dashboard} />
</Switch>
</BrowserRouter>
);
};
return (
<MuiThemeProvider theme={createMuiTheme(themeDefault)}>
<div className="App">
<Header
logo="some-logo"
userEmail={"test#email"}
/>
... app components here..
<Footer />
</div>
</MuiThemeProvider>
);
}
export default App;
Shared component
import React from "react";
import {
Box,
AppBar,
Toolbar,
Typography,
} from "#material-ui/core/";
import styles from "./Header.styles";
import PropTypes from "prop-types";
const Header = (props) => {
const classes = styles();
const { options, history } = props;
const [anchorEl, setAnchorEl] = React.useState(null);
const handleCloseMenu = () => {
setAnchorEl(null);
};
const goto = (url) => {
history.push(url);
};
return (
<Box component="nav" className={classes.headerBox}>
<AppBar position="static" className={classes.headerPart}>
<Toolbar className={classes.toolBar}>
{localStorage && localStorage.getItem("isLoggedIn") && (
<>
{options &&
options.map((option) => (
<Typography
key={option.url}
variant="subtitle1"
className={classes.headerLinks}
onClick={() => goto(option.url)}
>
{option.name}
</Typography>
))}
</>
)}
</Toolbar>
</AppBar>
</Box>
);
};
Header.propTypes = {
options: PropTypes.array
};
export default Header;
Shared Component style
import { makeStyles } from "#material-ui/core/styles";
export default makeStyles((theme) => ({
headerPart: {
background: "white",
boxShadow: "0px 4px 15px #00000029",
opacity: 1,
background: `8px solid ${theme.palette.primary.main}`
borderTop: `8px solid ${theme.palette.primary.main}`
}
}));
The Parent component defined theme.palette.primary.main as say Red color and I expect same to be applied in Header but it is picking a different theme (default) object which has theme.palette.primary.main blue.
Which results in my header to be in blue color but body in read color.
Any suggestion how to configure this theme object so that header too picks the theme.palette.primary.main from parent theme object.
here is the answer for mui V5
import { useTheme } from '#mui/material/styles' // /!\ I fixed a typo from official doc here
function DeepChild() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
Taken from mui documentation
You can use either useTheme or withTheme to inject the theme object to any nested components inside ThemeProvider.
Use useTheme hook in functional components
Use withTheme HOC in class-based components (which can't use hook)
function DeepChild() {
const theme = useTheme<MyTheme>();
return <span>{`spacing ${theme.spacing}`}</span>;
}
class DeepChildClass extends React.Component {
render() {
const { theme } = this.props;
return <span>{`spacing ${theme.spacing}`}</span>;
}
}
const ThemedDeepChildClass = withTheme(DeepChildClass);
Live Demo

Switch between dark and light theme in non-material ui component

I am trying to introduce a theme switcher in my app. I have a lot of non-material-ui elements that I need the theme to reflect the changes on them.
The code below shows that I have a state that is called darkState that is set to true. The material ui components in my app reflect those changes but for example the div below does not get the dark color of the dark theme. What is that I am doing wrong in here?
import React, { useState } from "react";
import Header from "./components/Header.js";
import TopBar from "./components/TopBar.js";
import Sequence from "./components/Sequence.js";
import SecondaryWindow from "./components/SecondaryWindow.js";
import { MuiThemeProvider, createMuiTheme, makeStyles } from "#material-ui/core/styles";
import "./App.css";
import { MainContextProvider } from "./contexts/mainContext.js";
function App() {
const [darkState, setDarkState] = useState(true);
const palletType = darkState ? "dark" : "light";
const theme = createMuiTheme({
palette: {
secondary: {
main: "#0069ff",
},
type: palletType,
},
});
const useStyles = makeStyles((theme) => ({
root: {
paddingLeft: 80,
height: "100%",
backgroundColor: theme.palette.background.default,
},
}));
const classes = useStyles();
return (
<MuiThemeProvider theme={theme}>
<MainContextProvider>
<div className={classes.root}>
<Header />
<TopBar />
<Sequence />
<SecondaryWindow />
</div>
</MainContextProvider>
</MuiThemeProvider>
);
}
export default App;
Now I know the answer, in my example, the class root is not able to benefit from the custom-created theme that is provided by MuiThemeProvider. Instead, it uses the original theme that comes in Mui. To solve this, I separated that div into a component. This way, the theme context (custom-theme from MuiThemeProvider) can be accessed by the div. This way when I switch DarkState, colors update on Mui components and HTML elements based on the custom theme palette.
import React, { useContext, useState } from "react";
import Header from "./components/Header.js";
import TopBar from "./components/TopBar.js";
import Sequence from "./components/Sequence.js";
import SecondaryWindow from "./components/SecondaryWindow.js";
import { MuiThemeProvider, createMuiTheme, makeStyles } from "#material-ui/core/styles";
import "./App.css";
import { DndProvider } from "react-dnd";
import { HTML5Backend } from "react-dnd-html5-backend";
import { MainContextProvider } from "./contexts/mainContext.js";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
function AppContent() {
const useStyles = makeStyles((theme) => ({
root: {
paddingLeft: 80,
height: "100%",
backgroundColor: theme.palette.background.default,
},
}));
const classes = useStyles();
return (
<div className={classes.root}>
<Header />
<TopBar />
<Sequence />
<SecondaryWindow />
</div>
);
}
function App() {
const [darkState, setDarkState] = useState(true);
const palletType = darkState ? "dark" : "light";
const theme = createMuiTheme({
palette: {
secondary: {
main: "#0069ff",
},
type: palletType,
},
});
return (
<MuiThemeProvider theme={theme}>
<MainContextProvider>
<AppContent />
</MainContextProvider>
</MuiThemeProvider>
);
}
export default App;
It's because you only change the #material component not the CSS, to change the CSS Theme, you need to make variable for CSS for Dark Theme.
on :root declare all the light theme color and div.darkmode all the darkmode:
:root {
--color-bg: #fff;
--color-text: #000;
}
.div.darkmode {
--color-bg: #363636;
--color-text: #d1d1d1;
}
/** Usage */
.div {
color: var(--color-text);
background: var(--color-bg)
}
and make a condition on the div when the dark theme is true a new classname darkmode will be added to dive as you wrote above
<div className={`${classes.root} ${darkState && `darkmode`}`}>
<Header />
<TopBar />
<Sequence />
<SecondaryWindow />
</div>
I created an example for you here.
let us know if anything goes wrong!
workaround 2
if you're not doing any customer style by CSS file then this will work
import React from 'react';
import CssBaseline from '#material-ui/core/CssBaseline';
export default function MyApp() {
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
{/* The rest of your application */}
</MuiThemeProvider>
);
}
I would declare both variations of your theme as constants above the mounting or rendering. This way you are not literally creating a new theme ever time your theme swaps. I would have a state holding the reference to the MUI-Theme constant.
You can manipulate data-theme attribute to toggle the dark/light theme. Try it on StackBlitz.
Setting up themes
Use data-theme attribute to set the selected theme.
/* default theme (light) */
:root {
--primary-color: #302ae6;
--secondary-color: #536390;
--font-color: #424242;
--bg-color: #fff;
}
/* dark theme */
[data-theme='dark'] {
--primary-color: #9a97f3;
--secondary-color: #818cab;
--font-color: #e1e1ff;
--bg-color: #161625;
}
Switching theme in React Hooks
// App.js
const [theme, setTheme] = useState({
light: true,
});
const handleChangeTheme = (event) => {
setTheme({ ...theme, [event.target.name]: event.target.checked });
};
Set our data-theme attribute accordingly
const currentTheme = theme.light === true ? 'light' : 'dark';
document.documentElement.setAttribute('data-theme', currentTheme);

Am I on the right track with my Material UI AppBar?

I'm working on a header for my site, and want to use Material UI. The header includes a logo, some nav links, an avatar with dropdown, etc. My question revolves around styling the nav links right now. With Material-UI, do I need to apply classes to /every/ element I want to style? For example, I currently have an app that looks like this:
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import Avatar from '#material-ui/core/Avatar';
import CssBaseline from '#material-ui/core/CssBaseline';
import IconButton from '#material-ui/core/IconButton';
import Link from '#material-ui/core/Link';
import Menu from '#material-ui/core/Menu';
import MenuItem from '#material-ui/core/MenuItem';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import theme from './theme';
import { ThemeProvider } from '#material-ui/core/styles';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => ({
root: {
backgroundColor: '#202632',
},
headerLink: {
padding: '10px 20px',
}
}));
function App() {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const classes = useStyles();
return (
<div className="App">
<ThemeProvider theme={theme}>
<CssBaseline />
<AppBar className={classes.root}>
<Toolbar className={classes.logo}>
<img alt="Logo" src="/logo2.png" height={60} />
<Link className={classes.headerLink} href="/dashboard">
Solutions
</Link>
<Link className={classes.headerLink} href="/clientportal">
Features
</Link>
<Link className={classes.headerLink} href="/pricing">
Pricing
</Link>
<Link className={classes.headerLink}>
Our team
</Link>
<Link className={classes.headerLink}>
Blog
</Link>
<Avatar onClick={handleClick} onClose={handleClose}>
DM
</Avatar>
<Menu anchorEl={anchorEl} open={Boolean(anchorEl)}>
<MenuItem onClick={handleClose}>Item1</MenuItem>
<MenuItem onClick={handleClose}>Item2</MenuItem>
<MenuItem onClick={handleClose}>Item3</MenuItem>
</Menu>
</Toolbar>
</AppBar>
</ThemeProvider>
</div>
);
}
export default App;
The typical way I would style links is with CSS, using something like nav a { padding: 20px }, however that doesn't appear to be the idiomatic way with Material-UI. Maybe I have that assumption wrong so I wanted to verify here what the best approach for styling overrides looks like.
Thanks
You could create a SFC for the Link.
import React from ‘react’;
import Link from '#material-ui/core/Link';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles(theme => ({
headerLink: {
padding: '10px 20px',
}
}));
const AppBarLink = React.forwardRef(function AppBarLink({ children, className, ...props }, ref) {
const classes = useStyles();
return (
<Link
className={`${classes.headerLink} ${className}`}
ref={ref}
{...props}
>
{children}
</Link>
);
});
export AppBarLink;
For this particular scenario, I would use withStyles to create a styled version of Link. This will have the same effect as psyrendust's answer, but using withStyles gets rid of all the boilerplate.
import React from ‘react’;
import Link from '#material-ui/core/Link';
import { withStyles } from '#material-ui/core/styles';
const AppBarLink = withStyles({
root: {
padding: '10px 20px'
}
})(Link);
export AppBarLink;
Putting this in its own file and exporting it is only necessary if you want to leverage it from multiple files. If you are only using it in one file, then just define it there.
If you want to control props in addition to styles, you can leverage defaultProps as shown in the working example below:
import React from "react";
import Link from "#material-ui/core/Link";
import { withStyles } from "#material-ui/core/styles";
const AppBarLink = withStyles({
root: {
padding: "10px 20px"
}
})(Link);
AppBarLink.defaultProps = {
color: "secondary"
};
export default function App() {
return (
<div className="App">
<Link>Here is Link (no padding or color override)</Link>
<br />
<AppBarLink>Here is AppBarLink</AppBarLink>
<br />
<AppBarLink color="primary">
Here is AppBarLink with explicit color of primary
</AppBarLink>
</div>
);
}

Resources