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?
Related
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
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'm trying to apply typography changes to the theme using Material UI. But object changes are not working. However, the palette is working.
I tried to make some changes to the H3 variation and also to the default font size, but none of the changes work.
However, the colors on the palette work.
App.js
import React from "react";
import "./App.css";
import Header from "./components/ui/Header";
import { ThemeProvider } from "#material-ui/styles";
import theme from "./components/ui/Theme";
function App() {
return (
<ThemeProvider theme={theme}>
<Header />
</ThemeProvider>
);
}
export default App;
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";
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,
});
}
function Header() {
return (
<ElevationScroll>
<AppBar color="primary">
<Toolbar>
<Typography variant="h3" component="h3">
Nome de teste
</Typography>
<h3>Teste</h3>
Teste
</Toolbar>
</AppBar>
</ElevationScroll>
);
}
export default Header;
Theme.js
import { createTheme } from "#material-ui/core/styles";
const arcBlue = "#0B72B9";
const arcOrange = "#FFBA60";
export default createTheme({
typography: {
fontSize: 60,
h3: {
fontWeight: 500,
},
},
palette: {
common: {
blue: `${arcBlue}`,
orange: `${arcOrange}`,
},
primary: {
main: `${arcBlue}`,
},
secondary: {
main: `${arcOrange}`,
},
},
});
If anyone can help, I would be very grateful.
Solution
I was importing from:
import { ThemeProvider } from "#material-ui/styles";
However, according to the documentation, it needs to be:
import { ThemeProvider } from "#mui/material/styles";
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>
);
};
I have the following component, that is build with https://material-ui-next.com/.
import React from 'react';
import { AppBar, Toolbar } from 'material-ui';
import { Typography } from 'material-ui';
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';
import {lightBlue} from 'material-ui/colors';
const theme = createMuiTheme({
palette: {
primary: {
main:lightBlue['A700']
},
text: {
primary: '#fff',
}
},
});
const View = (props) => (
<MuiThemeProvider theme={theme}>
<AppBar position="static">
<Toolbar>
<Typography variant="title">
{props.title}
</Typography>
</Toolbar>
</AppBar>
</MuiThemeProvider>
);
export default View;
I am trying to write a test for it:
import React from 'react';
import { shallow } from 'enzyme';
import View from '../Views/View';
import { Typography } from 'material-ui';
it('renders', () => {
const wrapper = shallow(<View title='Welcome' />);
expect(wrapper.find('Typography').text()).toEqual('Welcome');
});
How to write a test for a component, that is using material-ui components? In the case above, I tried to figure out, if the component contains Welcome.
I read https://material-ui-next.com/guides/testing/, but it is not clear, how can I write a test.
UPD: API changed from shallow to mount
Did you tried to use their API described here?
Probably your test can look something like this:
import React from 'react';
import { createMount } from 'material-ui/test-utils';
import View from '../Views/View';
import { Typography } from 'material-ui';
it('renders', () => {
const mount = createMount();
const wrapper = mount(<View title='Welcome' />);
expect(wrapper.find('Typography').text()).toEqual('Welcome');
});