get child state in stateless component using hooks - reactjs

README:
Hi! I'm new to using React, and even newer to using hooks so please correct me if my verbiage is incorrect. In fact, I was struggling to even google my issue/come up with a title for this post - how would I best put this problem into words?
Question :
I have a root component which contains a table in its state, and I'm using Material UI and react-csv to create a NavBar with a save button that can save the table. Material UI makes use of hooks; I know if my NavBar component was stateful I could write data={this.props.table} to get the table, but I was wondering how I would download the table given the current framework? Is it possible?
CodePen: https://codesandbox.io/embed/old-dust-88mrp
Root Component:
import React from "react";
import ReactDOM from "react-dom";
import NavBar from "./NavBar";
import "./styles.css";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
table: "this is a table"
};
}
render() {
return (
<div>
<NavBar />
<div>{this.state.table}</div>
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
NavBar:
[I tried to simplify code as much as possible!]
import React from "react";
import { withStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import IconButton from "#material-ui/core/IconButton";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import ListItemText from "#material-ui/core/ListItemText";
import SaveIcon from "#material-ui/icons/Save";
import Tooltip from "#material-ui/core/Tooltip";
import { CSVLink } from "react-csv";
const StyledMenu = withStyles({
paper: {
border: "1px solid #d3d4d5"
}
})(props => (
<Menu
elevation={0}
getContentAnchorEl={null}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
{...props}
/>
));
const StyledMenuItem = withStyles(theme => ({
root: {
"&:focus": {
backgroundColor: theme.palette.primary.main,
"& .MuiListItemIcon-root, & .MuiListItemText-primary": {
color: theme.palette.common.white
}
}
}
}))(MenuItem);
export default function PrimarySearchAppBar() {
const [anchorEl, setAnchorEl] = React.useState(null);
const handleClick = event => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
return (
<div>
<AppBar position="static">
<Toolbar>
<div>
<Tooltip disableFocusListener title="Save">
<IconButton size="medium" onClick={handleClick} color="inherit">
<SaveIcon />
</IconButton>
</Tooltip>
<StyledMenu
id="customized-menu"
anchorEl={anchorEl}
keepMounted
open={Boolean(anchorEl)}
onClose={handleClose}
>
<StyledMenuItem>
{/* In stateful components I could put this.props.table here,
but how does this translate to a stateless component? */}
<CSVLink data={"this is a test"}>
<ListItemText primary="Data" />
</CSVLink>
</StyledMenuItem>
</StyledMenu>
</div>
</Toolbar>
</AppBar>
</div>
);
}
Thanks for any advice/help!!

<NavBar table={this.state.table}/>
export default function PrimarySearchAppBar({table}) {
<CSVLink data={table}>
}

Related

I'm getting an "Uncaught TypeError: Cannot read properties of undefined (reading 'push')" with react-router-dom v6

I'm getting that error Uncaught TypeError: Cannot read properties of undefined (reading 'push'). I've seen some other posts about it but I'm still stuck. I'm using react-router-dom v6. I think it has something to do with passing the props or something like that. The immediate place it's happening is in the "Home" page, if the Login button is clicked, the error will happen and the browser will not redirect to the Login page. Here's my file hierarchy
src
components
Dashboard
Dashboard.js
Home
Home.js
Login
Login.js
Panel
Panel.js
App.js
index.js
The home page has a button to go to the login page. The login page has a button to go to Dashboard. Dashboard has a button to go to Panel.
Here's the code
index.js
import App from "./components/App";
App.js
import React, { Component } from "react";
import { render } from 'react-dom';
import Login from "./Login/Login";
import Home from "./Home/Home";
import Dashboard from "./Dashboard/Dashboard";
import Panel from "./Panel/Panel";
import {
BrowserRouter as Router,
Routes,
Route,
} from "react-router-dom";
export default class App extends Component {
constructor(props){
super(props)
}
render(){
return(
<Router>
<Routes>
<Route path="/frontend/" element={<Home />}></Route>
<Route path="/frontend/panel" element={<Panel />} />
<Route path="/frontend/login" element={<Login />}/>
<Route path="/frontend/dashboard" element={<Dashboard />}/>
</Routes>
</Router>
);
}
}
const appDiv = document.getElementById("app");
render(<App />, appDiv);`
Home
import React from 'react';
import { Container, Button } from '#material-ui/core';
import { makeStyles } from '#material-ui/core/styles';
const useStyles = makeStyles({
root: {
marginLeft: "auto",
marginRight: "auto",
width: "54%",
textAlign: "center"
},
});
const Home = (props) => {
const handleLoginClick = () => {
props.history.push('/frontend/login');
}
const classes = useStyles();
return (
<Container className={classes.root}>
<h1>Home Page</h1>
<Button variant="contained" onClick={handleLoginClick}>Login</Button>
</Container>
)
}
export default Home;
Login
import React, { useState } from 'react';
import { makeStyles } from '#material-ui/core/styles';
import {
Card,
CardMedia,
CardContent,
CardActions,
TextField,
Button
} from '#material-ui/core';
import './Login.css';
const useStyles = makeStyles({
root: {
marginLeft: "auto",
marginRight: "auto",
width: "40%",
},
media: {
height: 100,
width: 250,
marginLeft: "auto",
marginRight: "auto",
},
signInButton: {
marginLeft: "auto",
marginRight: "auto"
}
});
const Login = (props) => {
const [username, setUsername] = useState('');
const [password, setPassword] = useState('');
const classes = useStyles();
const handleLogin = () => {
props.history.push('/frontend/dashboard');
}
return (
<div>
<Card className={classes.root}>
<CardMedia className={classes.media} image="../../../static/images/some_image.jpg" title="Some Image"/>
<CardContent>
<TextField type="text" placeholder="Username" variant="filled" fullWidth></TextField>
<TextField type="password" placeholder="Password" variant="filled" fullWidth></TextField>
</CardContent>
<CardActions>
<Button variant="contained" className={classes.signInButton} onClick={handleLogin}>Sign In</Button>
</CardActions>
</Card>
</div>
);
}
export default Login;
Dashboard.js
import React from 'react';
import { makeStyles } from '#material-ui/core/styles';
//import { getUser, removeUserSession } from './Utils/Common';
import Drawer from '#material-ui/core/Drawer';
import CssBaseline from '#material-ui/core/CssBaseline';
import AppBar from '#material-ui/core/AppBar';
import Toolbar from '#material-ui/core/Toolbar';
import List from '#material-ui/core/List';
import Typography from '#material-ui/core/Typography';
import Divider from '#material-ui/core/Divider';
import ListItem from '#material-ui/core/ListItem';
import ListItemText from '#material-ui/core/ListItemText';
import { Button } from '#material-ui/core';
const drawerWidth = 240;
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
appBar: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
},
drawer: {
width: drawerWidth,
flexShrink: 0,
},
drawerPaper: {
width: drawerWidth,
},
// necessary for content to be below app bar
toolbar: theme.mixins.toolbar,
content: {
flexGrow: 1,
backgroundColor: theme.palette.background.default,
padding: theme.spacing(3),
},
}));
const Dashboard = (props) => {
const classes = useStyles();
//const user = getUser();
const handleLogout = () => {
//removeUserSession();
props.history.push('/frontend');
}
const handleClickCreateGraphs = () => {
props.history.push('/frontend/panel');
}
return(
<div>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<Typography variant="h6" noWrap>
Welcome
</Typography>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="permanent"
classes={{
paper: classes.drawerPaper,
}}
anchor="left"
>
<div className={classes.toolbar} />
<Divider />
<List>
<ListItem button key="View Graphs">
<ListItemText primary="View Graphs" />
</ListItem>
<ListItem button key="Create Graphs" onClick={handleClickCreateGraphs}>
<ListItemText primary="Create Graphs" />
</ListItem>
</List>
<Divider />
<Divider />
<Button variant="contained" onClick={handleLogout}>Logout</Button>
</Drawer>
</div>
)
}
export default Dashboard;
Panel
The panel page is way too long to post here and irrelevant to the issue. Right now, it has no onclick events to take you to another page
In react-router-dom V6 you can use useNavigate hook for programmatic navigation, like this:
Home
...
import { useNavigate } from "react-router-dom";
...
const Home = (props) => {
const navigate = useNavigate();
const handleLoginClick = () => {
navigate('/frontend/login');
}
...
}

MUI v5 cant' change primary color of any components [duplicate]

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

TypeError: Cannot read properties of undefined (reading 'breakpoints') in material ui [duplicate]

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 a react beginner. I use hooks in the class and the program reports errors

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

Invoke a function inside a React stateless component

I'm trying to invoke the function ButtonAppBar inside my stateless component but the TS compiler gives me this error: '{' expected. I'm not sure whether I should be passing it to my New Header component or whether I should give it a type.
Here's my component
import * as React from "react";
import { withStyles, createStyles } from "#material-ui/core/styles";
import AppBar from "#material-ui/core/AppBar";
import Toolbar from "#material-ui/core/Toolbar";
import Typography from "#material-ui/core/Typography";
import Button from "#material-ui/core/Button";
import IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
const styles: any = createStyles({
root: {
flexGrow: 1
},
header: {
backgroundColor: "#007"
},
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
}
});
const ButtonAppBar = (styles) => {
const classes = styles;
return (
<div className={classes.root}>
<AppBar position="static" className={classes.header}>
<Toolbar>
<IconButton
className={classes.menuButton}
color="inherit"
aria-label="Menu"
>
<MenuIcon />
</IconButton>
<Button color="inherit">Home</Button>
<Button color="inherit">Help</Button>
</Toolbar>
</AppBar>
</div>
);
}
const NewHeader: React.StatelessComponent<props> = ({}) => {
return (
{ButtonAppBar()}
);
}
export default withStyles(styles, NewHeader);
Your code is not parsed properly.
const NewHeader: React.StatelessComponent<props> = ({}) => ButtonAppBar();
either:
const NewHeader: React.StatelessComponent<props> = ({}) => {
return ButtonAppBar();
}

Resources