Invalid hook call - reactjs

It looks like a common problem but I can't solve it. I'm using React, TypeScript and Material-UI. All together do not work if I'm using classes (that's what TS is for). Here is my minimal example that is not working:
File RTaskForm.tsx
import * as React from "react"
import {Task, TaskProcessor} from "./Task"
import { makeStyles, Grid, TextField } from '#material-ui/core'
const styles = makeStyles(theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
},
dense: {
marginTop: theme.spacing(2),
},
menu: {
width: 200,
},
}));
export class RTaskForm extends React.Component<
{prefix: string},
{task: Task}> {
constructor(props: any) {
super(props)
this.state = {
task: TaskProcessor.tasks(false)[1]
}
}
render() {
const classes = styles()
return (
<form className={classes.container} noValidate autoComplete="off">
<Grid container spacing={2}>
<Grid item xs={12} sm={6}>
<TextField
required
id="filled-required"
label="Required"
defaultValue="Hello World"
className={classes.textField}
margin="normal"
variant="filled"
/>
</Grid>
</Grid>
</form>
)
}
}
File index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { RTaskForm } from "./RTaskForm"
ReactDOM.render(<div id="root">
<RTaskForm prefix="Hi"/>
</div>, document.getElementById('root'));
File Task.ts (only two classes to allow compiling)
export class Task extends Map<string, string> {
static emptyTask() {
return new Task()
}
}
export class TaskProcessor {
static tasks(b: boolean): Task[] {
return [Task.emptyTask()]
}
}
I know I have to use withStyles instead of makeStyles, but I have to provide a function parameter for theme, null is not accepted.
Happy to get some help, thanks.
Edit: In the browser I get this error message (line 36 marked):
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 "cannot write the link here" for tips about how to debug and fix this problem.
3 stack frames were collapsed.
render
src/RTaskForm.tsx:36
33 | }
34 |
35 | render() {
36 | const classes = styles()
| ^ 37 | return (
38 | <form className={classes.container} noValidate autoComplete="off">
39 | <Grid container spacing={2}>
View compiled

Related

ReactJS Invalid hook call while add useStyle variable

I am trying to write a from but it throws me this error:
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
This is App.js file where i am writing the form:
import React, { Component } from 'react';
import TextField from '#material-ui/core/TextField';
import './App.css';
import useStyles from '../src/styleMaker'
import { makeStyles } from '#material-ui/core/styles';
class App extends Component {
state = {
ssn: '',
}
useStyles = makeStyles(theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: theme.spacing(1),
marginRight: theme.spacing(1),
width: 200,
},
dense: {
marginTop: 19,
},
menu: {
width: 200,
},
paper: {
padding: theme.spacing(2),
textAlign: 'center',
color: theme.palette.text.secondary,
},
button: {
margin: theme.spacing(1),
},
}));
classes = useStyles();
onHandleChange(event) {
this.setState({
[event.target.name]: event.target.value
});
}
render() {
return (
<React.Fragment>
<form noValidate autoComplete="off">
<TextField
id=""
label="SSN"
value={this.state.ssn}
onChange={(event) => this.onHandleChange(event)}
type="number"
name='ssn'
margin="normal"
className={this.classes.textField}
/>
<TextField
id=""
label="SSN"
value={this.state.phone}
onChange={(event) => this.onHandleChange(event)}
type="number"
name='phone'
margin="normal"
className={this.classes.textField}
/>
</form>
</React.Fragment>
);
}
}
export default App;
Can anyone help me whats wrong with my code? Why my code is not working?
When i add userStyle, then i throws me the error, Can anyone help me in this case?
I spend hour to fix this issue but now i just give up on it. Anyone helps will make my day.. Thanks in advance
As the error is suggesting you
Hooks can only be called inside of the body of a function component
You are using a class based component here, you can convert this class based component to functional component, you would also need to use useState Hook for your state in functional Component, you can do this,
const App = () => {
const [ssn, setSsn ] = useState('')
const classes = useStyles()
// remaining code
return (
....
)
}
Also when you use functional component you cannot use this inside functional component
Hope it helps
You can not use hooks from within class based components. If you want to use hooks, you need to convert your components to functional, something along the following lines:
function App() {
const classes = useStyles();
...
return (
...
)
}

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

Material-UI withStyles not adding classes to props

I'm trying to implement some styling using Material-UI's withStyles method, however I'm not able to get classes as a prop. Any suggestions as to what I'm missing? I've included the relevant code below, but note that there is an <App> component in this file that I'm leaving out for brevity.
import React from 'react'
import ReactDOM from "react-dom";
import {Paper, Typography} from '#material-ui/core'
import {withStyles} from '#material-ui/core/styles'
import NavBar from "./navBar";
class Recipe extends React.Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log('Recipe Did Mount')
}
render() {
const {recipeData, classes} = this.props;
return (
<Paper>
<Typography className={classes.recipeName}>{recipeData.name}</Typography>
<Typography className={classes.recipeIngredients}>{recipeData.ingredients}</Typography>
<Typography className={classes.recipeInstructions}>{recipeData.instructions}</Typography>
</Paper>
)
}
}
const styles = {
root: {
fontSize: "1.0rem",
margin: "0px"
},
recipeName: {
fontSize: "1.0rem",
margin: "0px"
},
recipeIngredients: {
fontSize: "1.0rem",
margin: "0px" },
recipeInstructions: {
fontSize: "1.0rem",
margin: "0px" }
};
withStyles(styles)(Recipe);
document.addEventListener('DOMContentLoaded', () => {
ReactDOM.render(
<App/>,
document.body.appendChild(document.createElement('div')),
)
});
Since you aren't setting withStyles(styles)(Recipe); into a variable, I suspect you must be using Recipe directly within App.
withStyles doesn't change Recipe. withStyles creates a new component that wraps Recipe and passes the classes prop to it. In order to see the classes prop, you need to use the newly-created component with something like the following:
const StyledRecipe = withStyles(styles)(Recipe);
const App = ()=> {
return <StyledRecipe/>;
}
Assuming App is defined in a separate file (for others who may come looking for this question), change the
`withStyles(styles)(Recipe);`
To
export default withStyles(styles)(Recipe);
As Ryan already explained ' withStyles is the higher order component that creates and returns a new component'

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();
});
});

WithStyles not working in Material UI for React

I have an app using Material UI Beta where I try to style a simple component as follows:
import { MuiThemeProvider } from 'material-ui/styles';
const styles = theme => ({
container: {
display: 'flex',
flexWrap: 'wrap',
},
textField: {
marginLeft: 200,
marginRight: theme.spacing.unit,
width: 200,
},
menu: {
width: 200,
},
});
export const CreateJob = (props) => {
const { classes } = props;
let confirmDelete = () => {
const r = window.confirm("Confirm deletion of job");
return r === true;
};
return (
<MuiThemeProvider>
<div>
<form onSubmit={props.isEditting ? props.handleEdit : props.handleSubmit} noValidate autoComplete="off">
<h2>Update job details</h2>
<TextField
error={props.jobIdError !== ''}
helperText={props.jobIdError || "Example: ES10"}
autoFocus
margin="dense"
id="jobId"
label="Job ID"
name="jobid"
fullWidth
onChange={props.handleInputChange('jobId')}
value={props.jobId} />
</form>
</div>
</MultiThemeProvider>
I then use this in my parent component as follows:
<CreateJob open={this.state.open} />
However, this yields the following error:
TypeError: Cannot read property 'classes' of undefined
this.state is not defined in your code. In the example, state is defined as
state = {
name: 'Cat in the Hat',
age: '',
multiline: 'Controlled',
currency: 'EUR',
};
Sorry I'm kinda late with an answer, but I just found this question while searching for another solution.
I'm going to assume you also imported withStyles.
Firstly, you don't need to export both the simple component and the enhanced one:
export const CreateJob = props => {...} // lose the 'export'
export default withStyles(styles)(CreateJob); // only export here
Secondly, a real problem: <MuiThemeProvider> should be placed around your highest component(usually the <App> component that you render in your entry point file), so you can customize the default theme to your liking for the whole app; see their example here. I'm not sure, but this might even solve your problem, since that should have thrown another error like in this issue.
I just hope this helps someone, but I cannot be sure about what your exact problem is without the complete component file.

Resources