I am a react beginner. I use hooks in the class and the program reports errors
src\page\App.js
Line 41:35: React Hook "React.useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Line 44:39: React Hook "React.useState" cannot be called in a class component. React Hooks must be called in a React function component or a custom React Hook function react-hooks/rules-of-hooks
Search for the keywords to learn more about each error.
this is my code
import './App.css';
import React from "react";
import {BottomNavigation, BottomNavigationAction} from "#material-ui/core";
import {AccountCircle, Home, Info} from "#material-ui/icons";
import makeStyles from "#material-ui/core/styles/makeStyles";
import Grid from "#material-ui/core/Grid";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import styled from "#material-ui/core/styles/styled";
class App extends React.Component {
render() {
makeStyles(() => ({
title: {
color: "#ffffff"
},
toolbar: {
background: "#3f51b5"
},
contents: {
marginBottom: "100px"
},
bottomNavigation: {
left: '0px',
right: '0px',
position: 'fixed',
bottom: '0px',
}
}));
const {classes} = this.props
const MyGrid = styled(Grid)({
marginTop: 10,
});
//切换标签选中的钩子
const [value, setValue] = React.useState('home');
//切换toolbar的钩子
const [tbValue, setTbValue] = React.useState("主页");
const handleChange = (event, newValue) => {
setValue(newValue);
console.log("switch" + newValue)
//切换tb的字
switch (newValue) {
case 'home':
setTbValue("主页");
this.props.history.push('/home/user_login');
break;
case 'info':
setTbValue("信息");
this.props.history.push('/home/info');
break;
case 'me':
setTbValue("我的");
this.props.history.push('/home/me');
break;
}
};
return (
<div>
<AppBar position="static" style={{left: "0", right: "0"}}>
<Toolbar className={classes.toolbar}>
<Typography className={classes.title}>
{tbValue}
</Typography>
</Toolbar>
</AppBar>
{/*内容区*/}
<div className={classes.contents}>
<div>
<MyGrid container direction={'column'} justify={"center"}
alignContent={"center"}>
{this.props.children}
</MyGrid>
</div>
</div>
<BottomNavigation className={classes.bottomNavigation}
value={value}
onChange={handleChange} showLabels>
<BottomNavigationAction label="主页" value='home' icon={<Home/>}/>
<BottomNavigationAction label="信息" value='info' icon={<Info/>}/>
<BottomNavigationAction label="我的" value='me' icon={<AccountCircle/>}/>
</BottomNavigation>
</div>
);
}
}
export default App;
When i use method everything is normal
import './App.css';
import React from "react";
import {BottomNavigation, BottomNavigationAction} from "#material-ui/core";
import {AccountCircle, Home, Info} from "#material-ui/icons";
import makeStyles from "#material-ui/core/styles/makeStyles";
import Grid from "#material-ui/core/Grid";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import styled from "#material-ui/core/styles/styled";
function App(props) {
makeStyles(() => ({
title: {
color: "#ffffff"
},
toolbar: {
background: "#3f51b5"
},
contents: {
marginBottom: "100px"
},
bottomNavigation: {
left: '0px',
right: '0px',
position: 'fixed',
bottom: '0px',
}
}));
const {classes} = props
const MyGrid = styled(Grid)({
marginTop: 10,
});
//切换标签选中的钩子
const [value, setValue] = React.useState('home');
//切换toolbar的钩子
const [tbValue, setTbValue] = React.useState("主页");
const handleChange = (event, newValue) => {
setValue(newValue);
console.log("switch" + newValue)
//切换tb的字
switch (newValue) {
case 'home':
setTbValue("主页");
props.history.push('/home/user_login');
break;
case 'info':
setTbValue("信息");
props.history.push('/home/info');
break;
case 'me':
setTbValue("我的");
props.history.push('/home/me');
break;
}
};
return (
<div>
<AppBar position="static" style={{left: "0", right: "0"}}>
<Toolbar className={classes.toolbar}>
<Typography className={classes.title}>
{tbValue}
</Typography>
</Toolbar>
</AppBar>
{/*内容区*/}
<div className={classes.contents}>
<div>
<MyGrid container direction={'column'} justify={"center"}
alignContent={"center"}>
{props.children}
</MyGrid>
</div>
</div>
<BottomNavigation className={classes.bottomNavigation}
value={value}
onChange={handleChange} showLabels>
<BottomNavigationAction label="主页" value='home' icon={<Home/>}/>
<BottomNavigationAction label="信息" value='info' icon={<Info/>}/>
<BottomNavigationAction label="我的" value='me' icon={<AccountCircle/>}/>
</BottomNavigation>
</div>
);
}
export default App;
Using hooks in class component is not allowed. As stated in the react docs:
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
Link: https://reactjs.org/docs/hooks-faq.html#should-i-use-hooks-classes-or-a-mix-of-both
Related
In the demo.js, I wanted to use the Card along with its CardHeader and put wordings inside the CardContent. Also, I will be using the CardComponent in other files as well. How can I make the CardComponent reusable?
Sample codes: https://codesandbox.io/s/basiccard-material-demo-forked-kkshx?file=/demo.js
Below are the codes for the demo.js:
import * as React from "react";
import CardComponent from "./CardComponent";
export default function BasicCard() {
return (
<>
<h1>Hello</h1>
<CardComponent />
</>
);
}
Below are the codes for the CardComponent:
import React from "react";
import Card from "#mui/material/Card";
import CardContent from "#mui/material/CardContent";
import { CardHeader } from "#mui/material";
const CardComponent = () => {
const CardStyle = {
maxWidth: 500,
margin: "0 auto",
marginBottom: "1rem",
marginTop: "1rem"
};
return (
<Card sx={{ minWidth: 275 }} elevation={5} style={CardStyle}>
<CardHeader title="Card Header Title here" />
<CardContent>//content here</CardContent>
</Card>
);
};
export default CardComponent;
I have another question as well under this component. Coming from material-ui4, mui5 was kind of confusing as I can no longer use the makeStyles. I tried adding this in the codesandbx example, however, it will say that dependency not found:
import { makeStyles } from '#mui/styles';
Hence, I settled with const CardStyle. Any help on how can I implement the newer version of the makeStyles? Also, do I need to install other dependecies from material-ui to make it work? Below is the package.json file.
Also in: https://www.reddit.com/r/reactjs/comments/sgqhh3/how_do_i_make_a_reusable_component_for_the_cards/
You can make it reusable adding props to your child card component. You can add as many props you want to make different cards using the same pattern.
To give styles to the component in mui v5 you have to use styled. You have to import it from #mui/material/styles
Here is the whole code of the new card component
import React from "react";
import {styled} from "#mui/material/styles"
import Card from "#mui/material/Card";
import CardContent from "#mui/material/CardContent";
import { CardHeader } from "#mui/material";
const StyledCard = styled((props) => (
<Card
{...props}
/>
))(({ theme }) => ({
maxWidth: 500,
margin: "0 auto",
marginBottom: "1rem",
marginTop: "1rem"
}));
const CardComponent = (props) => {
const {title,content}=props
return (
<StyledCard sx={{ minWidth: 275 }} elevation={5}>
<CardHeader title={title} />
<CardContent>{content}</CardContent>
</StyledCard>
);
};
export default CardComponent;
And here it is the whole codesandbox.
I have created a theme in the index of my React.JS project using MUI. When I try to apply my style to my Appbar the theme does not correctly modify the menu button nor the menu itself. the button looks generic default and the menu remains white when it should match the color of the Appbar itself.
My index.tsx looks as such:
import React from "react";
import ReactDOM from "react-dom";
import AppbarTop from "./AppbarTop";
import { Router } from "react-router";
import { createBrowserHistory } from "history";
import AdapterDateFns from "#mui/lab/AdapterDateFns";
import { LocalizationProvider } from "#mui/lab";
import { createTheme } from "#mui/material";
import { ThemeProvider } from "#mui/styles";
import { StyledEngineProvider } from "#mui/material/styles";
const customHistory = createBrowserHistory();
const theme = createTheme({
palette: {
primary: {
main: "#242526"
},
secondary: {
main: "#d975d0"
},
text: {
primary: "#E4E6EB",
secondary: "#B0B3B8"
},
background: {
default: "#242526",
paper: "#242526"
}
}
});
ReactDOM.render(
<React.StrictMode>
<LocalizationProvider dateAdapter={AdapterDateFns}>
<Router history={customHistory}>
<ThemeProvider theme={theme}>
<StyledEngineProvider injectFirst>
<AppbarTop />
</StyledEngineProvider>
</ThemeProvider>
</Router>
</LocalizationProvider>
</React.StrictMode>,
document.getElementById("root")
);
My appbar.tsx looks like this:
import React from "react";
import {
AppBar,
Box,
Button,
Container,
Menu,
MenuItem,
Toolbar
} from "#mui/material";
import HomeIcon from "#mui/icons-material/Home";
import { makeStyles } from "#mui/styles";
const useStyles = makeStyles((theme?: any) => ({
appBar: {
background: theme.palette.primary.main,
height: "60px",
position: "relative"
}
}));
const AppbarTop: React.FC<{ [key: string]: any }> = () => {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState<any>(null);
const open = Boolean(anchorEl);
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<>
<AppBar position="static" className={classes.appBar}>
<Toolbar>
<Button
id="basic-button"
aria-controls="basic-menu"
aria-haspopup="true"
aria-expanded={open ? "true" : undefined}
onClick={handleClick}
>
Dashboard
</Button>
<Menu
id="basic-menu"
anchorEl={anchorEl}
open={open}
onClose={handleClose}
MenuListProps={{
"aria-labelledby": "basic-button"
}}
>
<MenuItem onClick={handleClose}>
<HomeIcon />{" "}
</MenuItem>
</Menu>
{/*test speed dial*/}
<Container maxWidth="sm"></Container>
<Box></Box>
</Toolbar>
</AppBar>
</>
);
};
export default AppbarTop;
Can someone please explain what I am missing?
Change this line:
import { ThemeProvider } from "#mui/styles";
To:
import { ThemeProvider } from "#mui/material/styles";
Reason: There are 2 ThemeProviders here
The one from #mui/styles: This ThemeProvider does send the Theme object down via context, it works fine, you can still access it using the useTheme hook:
const theme = useTheme();
return <Box sx={{ width: 10, height: 10, bgcolor: theme.palette.primary.main }} />
The one from #mui/material/styles: This ThemeProvider is a wrapper of the above, but it also injects the theme to the StyledEngineThemeContext.Provider, which allows you to access the theme when using MUI API (sx prop/styled()). The problem here is that the Button and Menu components uses the styled() API under-the-hood so the ThemeProvider needs to be imported from #mui/material/styles to make it work.
return <Box sx={{ width: 10, height: 10, bgcolor: 'primary.main' }} />
Related answers
Difference between #mui/material/styles and #mui/styles?
Cannot use palette colors from MUI theme
MUI - makeStyles - Cannot read properties of undefined
Material UI Dark Mode
I am trying to export styled AppBar,this is my code
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import MuiAppBar from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import MenuIcon from '#mui/icons-material/Menu';
const MuiAppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})(({ theme, open }) => ({
zIndex: theme.zIndex.drawer + 1,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
marginLeft: drawerWidth,
width: `calc(100% - ${drawerWidth}px)`,
transition: theme.transitions.create(['width', 'margin'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
const AppBar = () => {
return (
<div>
<MuiAppBar position="fixed" open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{
marginRight: '36px',
...(open && { display: 'none' }),
}}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Mini variant drawer
</Typography>
</Toolbar>
</MuiAppBar>
</div>
)
}
export default AppBar
This is giving pre-declared error
Line 12:7: Parsing error: Identifier 'MuiAppBar' has already been declared.
You cannot import:
import MuiAppBar from '#mui/material/AppBar';
and then create a function expression with the same name. Just change the name of your function expression to something else and use that name in your jsx
example:
const CustomMuiAppBar = .....
///
<CustomMuiAppBar />
You have declared The appBar Styled function with the same name. You are already importing MuiAppBar from mui. You must choose another name for your custom function. For example:
import MuiAppBar from '#mui/material/AppBar';
const CustomAppBar = styled()
Here the custom function name is different from the default one. Try this, It should work.
I want to let the drawer disappear behind my navbar when it is opened. Currently, the drawer is overlaying the navbar. I tried to change it with z-index in my "styles" but apparently it does not work (see screenshot). I set the z-index for the navbar higher than for the drawer.
Can you identify my mistake?
Here is the drawer.js:
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core/styles";
import Drawer from "#material-ui/core/Drawer";
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: "flex",
},
drawer: {
width: drawerWidth,
zIndex: 400,
flexShrink: 0,
/*whiteSpace: "nowrap",*/
},
drawerOpen: {
width: drawerWidth,
transition: theme.transitions.create("width", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.enteringScreen,
}),
},
And here is my navbar.js:
// import packages
import React, { useContext, Fragment } from "react";
import { Link } from "react-router-dom";
// import local dependenceis
import Logo from "./images/tubot_logo.png";
import AuthContext from "../../Context/Auth/authContext";
// import material-ui
import withStyles from "#material-ui/core/styles/withStyles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
// styling
const styles = (theme) => {
return {
root: {
flexGrow: 1,
zIndex: 2000,
},
title: {
flexGrow: 1,
fontFamily: "shadowsintolight",
textDecoration: "none",
color: "black",
},
};
const Navbar = (props) => {
// destructuring: styling props
const {
root,
title,
} = props.classes;
// react: return jsx
return (
<div className={root}>
<AppBar position="absolute" >
<Toolbar>
/*some navbar stuff*/
</Toolbar>
</AppBar>
</div>
);
};
Try setting the classes prop on your Drawer component like this:
const App = () => {
const classes = useStyles();
return (
<div className="App" className={root}>
<AppBar position="absolute">
<Toolbar>
// Toolbar content...
</Toolbar>
</AppBar>
<Drawer
variant="persistent"
anchor="left"
open={true}
classes={{
paper: classes.drawer,
}}>
// Drawer content...
</Drawer>
</div>
);
};
In a component, i want that my variable from the store is equal a function in order to use it.
I create my function like that:
makeStyles(theme => ({
root: {
flexGrow: 1
},
title: {
flexGrow: 1
}
}));
It is a function from material-ui.
And i get the value like that in my store:
this.state = {
classes: makeStyles()
};
So i'm expected that i can use it like: this.state.classes.root but i don't works (it compile but don't works)
Thats my code:
import React, { Component } from "react";
import { makeStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import Typography from "#material-ui/core/Typography";
makeStyles(theme => ({
root: {
flexGrow: 1
},
title: {
flexGrow: 1
}
}));
class Navbar extends Component {
constructor() {
super();
this.state = {
classes: makeStyles()
};
}
render() {
return (
<div className={this.state.classes.root}>
<AppBar position="static">
<Toolbar className="red">
// for example, here i would like to use it like: this.state.classes.root
<div className="blue">
<IconButton edge="start" color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
</div>
<div className="green">
<Button color="inherit">Login</Button>
</div>
</Toolbar>
</AppBar>
</div>
);
}
}
const mapStateToProps = state => {
return {
access_token: state.access_token
};
};
export default withRouter(connect(mapStateToProps)(Navbar));
Instead of
<Toolbar className="red">
I would like to have:
<Toolbar className={this.state.classes.root}>
How should I go about doing this?
To use JavaScript values, you have do this way:
<Toolbar className={this.state.classes.root}>
Check by changing your code as:
(Changes are commented in CAPITAL)
import React, { Component } from "react";
import { makeStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import Typography from "#material-ui/core/Typography";
// INITIALIZE THE makeStyles INTO ANOTHER VARIABLE, HERE I USED useStyles
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1
},
title: {
flexGrow: 1
}
}));
class Navbar extends Component {
constructor() {
super();
// NOW CALL useStyles IN THE state
this.state = {
classes: useStyles()
};
}
render() {
return (
<div className={this.state.classes.root}>
<AppBar position="static">
<Toolbar className="red">
// for example, here i would like to use it like: this.state.classes.root
<div className="blue">
<IconButton edge="start" color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
</div>
<div className="green">
<Button color="inherit">Login</Button>
</div>
</Toolbar>
</AppBar>
</div>
);
}
}
const mapStateToProps = state => {
return {
access_token: state.access_token
};
};
export default withRouter(connect(mapStateToProps)(Navbar));
Please comment if in case you face any problem in implementing the solution.
I suggest using the Material-UI styles Hook API. Here's their short-form example:
import React from 'react';
import { makeStyles } from '#material-ui/styles';
import Button from '#material-ui/core/Button';
const useStyles = makeStyles({
root: {
background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
border: 0,
borderRadius: 3,
boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
color: 'white',
height: 48,
padding: '0 30px',
},
});
export default function Hook() {
const classes = useStyles();
return <Button className={classes.root}>Hook</Button>;
}
Your code would look like the following. Note I've converted it to a functional component so it can use Hooks.
import React, { Component } from "react";
import { makeStyles } from "#material-ui/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import { connect } from "react-redux";
import { withRouter } from "react-router-dom";
import Typography from "#material-ui/core/Typography";
const useStyles = makeStyles({
root: {
flexGrow: 1
},
title: {
flexGrow: 1
}
});
function NavBar({access_token}) {
const classes = useStyles();
render() {
return (
<div className={classes.root}>
<AppBar position="static">
<Toolbar className="red">
<div className={classes.root}>
<IconButton edge="start" color="inherit" aria-label="Menu">
<MenuIcon />
</IconButton>
</div>
<div className="green">
<Button color="inherit">Login</Button>
</div>
</Toolbar>
</AppBar>
</div>
);
}
}
const mapStateToProps = state => {
return {
access_token: state.access_token
};
};
export default withRouter(connect(mapStateToProps)(Navbar));
It doesn't work because you assign the this.state.classes.root value to className, I guess you intended to assign it to style?