React with MUI's useStyles() giving me a hooks rules violation - reactjs

I'm getting the dreaded React "Invalid Hook Call" error and I'm not running any hooks.
Installed React using CRA
Installed MUI
Built simple styles page:
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
justifyContent: 'center',
},
layout: {
minHeight: '230px',
},
}));
export default useStyles;
Added styles to component:
import React from 'react';
import useStyles from './styles';
import Grid from '#material-ui/core/Grid';
import Container from '#material-ui/core/Container';
const SimpleComponent = (props) => {
const classes = useStyles();
return (
<React.Fragment>
<Container className={classes.root}>
<Grid container spacing={3}>
<Grid item xs={12}>
<div>Page Content</div>;
</Grid>
</Grid>
</Container>
</React.Fragment>
);
};
export default SimpleComponent;
The app returns no errors on npm start, but the page itself gives me the invalid hook call pointing to the line const classes = useStyles();. That's not a hook.
I've tried re npm installing the app, I've tried moving the call all around, no matter what I do I get that same page. Looks like a lot of people have had similar problems with this configuration but no other solutions have addressed my problem.
Hooks Error Page:
Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See[https://reactjs.org/warnings/invalid-hook-call-warning.html][1] for tips about how to debug and fix this problem.
SimpleComponent
src/Components/PaymentHistory/PaymentHistory.js:7
4 | import Container from '#material-ui/core/Container';
5 |
6 | const SimpleComponent = (props) => {
> 7 | const classes = useStyles();
8 | return (
9 | <React.Fragment>
10 | <Container className={classes.root}>
View compiled

Please make sure all the dependencies(including #material-ui/core) are correctly installed.
Here is a working demo.

Related

How to access MUI 5 theme variables in deep functional component?

The MUI 5 docs on Theming have a section on "Accessing the theme in a component". However, it's really just one sentence that links to the legacy style docs.
Here's the example they give in those legacy docs:
import { useTheme } from '#mui/styles';
function DeepChild() {
const theme = useTheme();
return <span>{`spacing ${theme.spacing}`}</span>;
}
Which is pretty much exactly what I want to do — I want to be able to access the theme color palette down in some deep functional component. However, my component complains
Module not found: Error: Can't resolve '#mui/styles' in...
Digging a little further, it seems they're rather strongly trying to discourage people from using this legacy Styles technique, and the MUI 5 way to do this is with "system design tokens", which I guess should Just Work. But, they're not.
I have my whole app wrapped in ThemeProvider:
import React from 'react';
import { CssBaseline } from '#mui/material';
import { ThemeProvider } from '#mui/material/styles';
import theme from './theme';
import Foo from './foo';
const App = () => {
return (
<Fragment>
<ThemeProvider theme={theme}>
<CssBaseline enableColorScheme />
<Foo />
</ThemeProvider>
</Fragment>
);
};
export default App;
And then in foo.js:
import React from 'react';
import { Box } from '#mui/material';
export const Foo = () => {
return (
<Box
sx={{
background: 'repeating-linear-gradient(-45deg, '
+ ' theme.palette.error.light, theme.palette.error.light 25px,'
+ ' theme.palette.error.dark 25px, theme.palette.error.dark 50px'
+ ')',
}}
>
<span>Test</span>
</Box>
);
};
I initially started with just error.light and error.dark. When that didn't work, I expanded it all to palette.error.light, etc..., and then ultimately to theme.palette.error.light, etc....
It seems no matter what I try, it's not accessing those theme variables, and is instead just passing through the text.
So, back to the question: how am I supposed to access MUI 5 theme variables in nested functional components?
Replace
import { useTheme } from '#mui/styles';
with
import { useTheme } from '#mui/material/styles';
#mui/styles is used for legacy, you can add it using yarn add or npm install, but first give a shot to what I mentioned above.

I get the Error message: "Invalid hook call" when I try to run my React app. Can't understand why

I get the error message: Invalid hook call. Hooks can only be called inside of the body of a function component when I try to run my react app.
import React from "react";
import BarChart from "../components/BarChart";
import LineChart from "../components/LineChart";
import Navbar from "../components/Navbar";
import { makeStyles } from "#material-ui/core/styles";
const useStyles = makeStyles({
row: {
display: "flex",
flexDirection: "row"
}
});
const Index = () => {
const classes = useStyles();
return (
<div>
<Navbar />
<div className={classes.row}>
<BarChart />
<LineChart />
</div>
</div>
);
};
export default Index;
It fails in line 16 (const classes = useStyles();).
The Navbar, BarChart and LinChart components is just components that I have created and it doesn't seem like the code breaks in these components. I'm able to run my code without any error messages if iI remove line 16.
Any ideas what might help?
I tried makeStyles locally and it worked perfectly fine. Kindly check your dependencies and update them to latest then try again.
I'm currently having:
"react": "^16.8.2", "react-dom": "^16.8.2", "#material-ui/core": "^4.9.12"

Invalid hook call. Hooks can only be called inside of the body of a function component when apply style to class base component with material-ui

I am just started to learn reactjs using material-ui but getting this error when apply style to my component. My code:
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
},
menuButton: {
marginRight: theme.spacing(2),
},
title: {
flexGrow: 1,
},
}));
class NavMenu extends React.Component {
constructor(props) {
super(props);
this.state = {
isOpen: false
};
}
render() {
const classes = useStyles();
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar>
<IconButton
edge="start"
className={classes.menuButton}
color="inherit"
aria-label="Menu"
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
className={classes.title}
>
News
</Typography>
<Button color="inherit">Login</Button>
</Toolbar>
</AppBar>
</div>
);
}
}
export default NavMenu;
and this is Error:
material-ui makeStyles function only works inside function components, as it uses the new React Hooks APIs inside.
You have two options:
Convert your class component to a functional component.
Use a Higher Order Component as in material-ui docs
I personally recommend the first approach, as this is becoming the new standard in React development.
This tutorial may help you get started with functional components
and check the docs for React Hooks
Use withStyles:
App.js:
import {withStyles} from '#material-ui/core/styles'
// ...
const styles = theme => ({
paper: {
padding: theme.spacing(2),
// ...
},
// ...
})
class App extends React.Component {
render() {
const {classes} = this.props
// ...
}
}
export default withStyles(styles)(App)
Root.js:
import React, {Component} from 'react'
import App from './App'
import {ThemeProvider} from '#material-ui/styles'
import theme from '../theme'
export default class Root extends Component {
render() {
return (
<ThemeProvider theme={theme}>
<App/>
</ThemeProvider>
)
}
}
theme.js:
import {createMuiTheme} from '#material-ui/core/styles'
const theme = createMuiTheme({
palette: {
primary: ...
secondary: ...
},
// ...
}
export default theme
See Theming - Material-UI.
See Higher-order component API.
if you have created a functional component and still run into this issue... the next thing to look for are the dependency versions.
I tried a new stackblitz project to test a material-ui component and got this error. My dependencies were:
react 16.12
react-dom 16.12
#material-ui/core 4.9.14
So I had to change to latest react version using react#latest and react-dom#latest which got me to the following:
react 16.13.1
react-dom 16.13.1
#material-ui/core 4.9.14
Sharing here so that it can help other people who run into this... thanks to this post for the hint

unit testing material ui components with jest

There is a grid component in material ui and I want it to be unit tested using jest in my React application. Please find my code below
For the above code, how we will write unit test? I went through their testing guide https://material-ui.com/guides/testing, but it is not clear.Any ideas/suggestions are really appreciated
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "#material-ui/core/styles";
import Grid from "#material-ui/core/Grid";
import Paper from "#material-ui/core/Paper";
import "./styles.scss";
const styles = theme => ({
root: {
flexGrow: 1
},
paper: {
height: 140,
width: 100
}
});
function Space({ classes }) {
const x = [1, 2, 3];
return (
<div className="center">
<Grid container className={classes.root}>
<Grid item xs={12}>
<Grid container justify="center" spacing={Number(32)}>
{x.map(value => (
<Grid key={value} item>
<Paper className={classes.paper} />
</Grid>
))}
</Grid>
</Grid>
</Grid>
</div>
);
}
I should be able to unit test the above code using jest
People might hate me for saying this. Since this case does not contain any logic I would just export the function, setup props which reflect real data, and make a snapshot of your shallow rendered function.
Whenever it changes, check it before you update it and all is okay.
EDIT: export the normal component without the withStyles HOC and add the classes to your mocked props.
ANOTHER EDIT:
Your test should probably look something like this in this case:
describe('SpaceComponent', () => {
it('it renders the component correctly', () => {
const props = { classes: { root: 'root', paper: 'paper' } };
const component = shallow(<Space {...props} />);
expect(component).toMatchSnapshot();
});
});

How to extend prop types of Material UI components using Flow when passing through props?

I'd like to add Flow Types to an existing React project which uses Material UI.
I have a number of components which render a Material UI component and pass through any props received. For example:
import React from "react";
import IconButton from "#material-ui/core/IconButton";
import backIcon from "assets/icons/Back.svg";
const BackButton = ({ height, ...props }) => (
<IconButton {...props}>
<img
src={backIcon}
alt="back"
style={{
height,
width: height
}}
/>
</IconButton>
);
BackButton.defaultProps = {
height: "1.5rem"
};
export default BackButton;
If I were to define prop types for this, I figure it would look something like
type BackButtonProps = IconButtonProps & {
height: string
}
const BackButton = ({height, ...props}): BackButtonProps => (
...
)
My issue is that I'm not sure where to get IconButtonProps from. I've run flow-typed install and have a flow-typed/npm/#material-ui folder, but I can't figure out how to import IconButtonProps. I've tried the following:
import type { IconButtonProps } from "#material-ui/core/IconButton"
But the import is unresolved.
Where can I import the prop types of Material UI components from, in order to be able to extend them?

Resources