I recently started learning about the Material UI. To develop a basic website structure while checking the basic template code.
However, certain items in the menu bar are not on the right side as in the example. I have taken most of the code as is and merged it, and I wonder why it doesn't move to the right.
CodeSandBox URL
Here's my full code but it seems quite long so I also add codesandbox URL
Full Code
import React from "react";
import clsx from "clsx";
import { makeStyles, useTheme } from "#material-ui/core/styles";
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 IconButton from "#material-ui/core/IconButton";
import MenuIcon from "#material-ui/icons/Menu";
import ChevronLeftIcon from "#material-ui/icons/ChevronLeft";
import ChevronRightIcon from "#material-ui/icons/ChevronRight";
import ListItem from "#material-ui/core/ListItem";
import ListItemIcon from "#material-ui/core/ListItemIcon";
import ListItemText from "#material-ui/core/ListItemText";
import InboxIcon from "#material-ui/icons/MoveToInbox";
import MailIcon from "#material-ui/icons/Mail";
import Badge from "#material-ui/core/Badge";
import Menu from "#material-ui/core/Menu";
import MenuItem from "#material-ui/core/MenuItem";
import NotificationsIcon from "#material-ui/icons/Notifications";
import AccountCircle from "#material-ui/icons/AccountCircle";
import MoreIcon from "#material-ui/icons/MoreVert";
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: "flex"
},
appBar: {
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
})
},
appBarShift: {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: drawerWidth,
transition: theme.transitions.create(["margin", "width"], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
})
},
menuButton: {
marginRight: theme.spacing(2)
},
hide: {
display: "none"
},
drawer: {
width: drawerWidth,
flexShrink: 0
},
drawerPaper: {
width: drawerWidth
},
drawerHeader: {
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
...theme.mixins.toolbar,
justifyContent: "flex-end"
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen
}),
marginLeft: -drawerWidth
},
contentShift: {
transition: theme.transitions.create("margin", {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen
}),
marginLeft: 0
}
}));
export default function User() {
const classes = useStyles();
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
// TEST
const [anchorEl, setAnchorEl] = React.useState(null);
const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null);
const isMenuOpen = Boolean(anchorEl);
const isMobileMenuOpen = Boolean(mobileMoreAnchorEl);
const handleProfileMenuOpen = event => {
setAnchorEl(event.currentTarget);
};
const handleMobileMenuClose = () => {
setMobileMoreAnchorEl(null);
};
const handleMenuClose = () => {
setAnchorEl(null);
handleMobileMenuClose();
};
const handleMobileMenuOpen = event => {
setMobileMoreAnchorEl(event.currentTarget);
};
const menuId = "primary-search-account-menu";
const renderMenu = (
<Menu
anchorEl={anchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={menuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMenuOpen}
onClose={handleMenuClose}
>
<MenuItem onClick={handleMenuClose}>Profile</MenuItem>
<MenuItem onClick={handleMenuClose}>My account</MenuItem>
</Menu>
);
const mobileMenuId = "primary-search-account-menu-mobile";
const renderMobileMenu = (
<Menu
anchorEl={mobileMoreAnchorEl}
anchorOrigin={{ vertical: "top", horizontal: "right" }}
id={mobileMenuId}
keepMounted
transformOrigin={{ vertical: "top", horizontal: "right" }}
open={isMobileMenuOpen}
onClose={handleMobileMenuClose}
>
<MenuItem>
<IconButton aria-label="show 4 new mails" color="inherit">
<Badge badgeContent={4} color="secondary">
<MailIcon />
</Badge>
</IconButton>
<p>Messages</p>
</MenuItem>
<MenuItem>
<IconButton aria-label="show 11 new notifications" color="inherit">
<Badge badgeContent={11} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton>
<p>Notifications</p>
</MenuItem>
<MenuItem onClick={handleProfileMenuOpen}>
<IconButton
aria-label="account of current user"
aria-controls="primary-search-account-menu"
aria-haspopup="true"
color="inherit"
>
<AccountCircle />
</IconButton>
<p>Profile</p>
</MenuItem>
</Menu>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={clsx(classes.appBar, {
[classes.appBarShift]: open
})}
>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
className={clsx(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
LiveToday
</Typography>
<div className={classes.grow} />
<div className={classes.sectionDesktop}>
<IconButton aria-label="show 4 new mails" color="inherit">
<Badge badgeContent={4} color="secondary">
<MailIcon />
</Badge>
</IconButton>
<IconButton aria-label="show 17 new notifications" color="inherit">
<Badge badgeContent={17} color="secondary">
<NotificationsIcon />
</Badge>
</IconButton>
<IconButton
edge="end"
aria-label="account of current user"
aria-controls={menuId}
aria-haspopup="true"
onClick={handleProfileMenuOpen}
color="inherit"
>
<AccountCircle />
</IconButton>
</div>
<div className={classes.sectionMobile}>
<IconButton
aria-label="show more"
aria-controls={mobileMenuId}
aria-haspopup="true"
onClick={handleMobileMenuOpen}
color="inherit"
>
<MoreIcon />
</IconButton>
</div>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<List>
{["Inbox", "Starred", "Send email", "Drafts"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
<Divider />
<List>
{["All mail", "Trash", "Spam"].map((text, index) => (
<ListItem button key={text}>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</Drawer>
<main
className={clsx(classes.content, {
[classes.contentShift]: open
})}
>
<div className={classes.drawerHeader} />
</main>
{renderMobileMenu}
{renderMenu}
</div>
);
}
The div which is responsible for pushing icons to the right is using className={classes.grow}, but you haven't defined grow anywhere.
Add the following code to your makeStyles function:
grow: {
flexGrow: 1
}
Related
I have multiple rendered components on a page. When I click on one I want to navigate to a new page with all of the information specific to that component. What I need is:
OnClick -> Identify gameId variable
Using that gameID run a query against an API endpoint
Take the rendered data from the API and display it on a new page with route /nhl/:gameId
Example screenshot of /NHL page with different components.
/NHL Route
I have this existing Scores component:
function Scores(props) {
const navigate = useNavigate('/');
const handleClick = () => {
let path = '/' + {gameId};
navigate(path);
}
return (
<Card sx={{ Height: 50, padding: .25, margin: 3 }}>
<CardActionArea onClick={handleClick}>
<div className = "">
<CardContent sx={{ width: 200 }}>
<Stack direction="row" spacing={2}>
<Typography component="div" variant="h6">{props.gameStatus}</Typography>
<Typography>{props.gameDateandTime}</Typography>
</Stack>
<Stack direction="row" spacing={2}>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.awayTeam}</Typography>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.awayPoints}</Typography>
</Stack>
<Typography>#</Typography>
<Stack direction="row" spacing={2}>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.homeTeam}</Typography>
<Typography fontSize={14} sx={{ paddingTop: 1, paddingBottom: 1 }}>{props.homePoints}</Typography>
</Stack>
</CardContent>
</div>
</CardActionArea>
</Card>
)
}
export default Scores;
The Scores.js component above is rendered in the NHL route from the screenshot:
function NHL(){
var date = year + "-" + month + "-" + day;
const [scores, setScores] = useState([{ }]);
useEffect(() => {
let url = "http://localhost:3001/schednhl";
fetch(url).then(res => {
if(res.ok) {
return res.json()
}
}).then(jsonRes => setScores(jsonRes));
}, []);
return (
<div className='Home'>
<Banner />
<Typography variant="h3" align="center" sx={{ paddingTop: 5 }}>Live Games</Typography>
<hr className="lineRow"></hr>
<Stack direction="row" spacing={2}>
<div className="scroll-box">
<div className="scroll-box-wrapper">
<div className="scroll-box-container" role="list">
{scores.map((score, index) => {
if(score.gameDate === date && score.gameStatus === "closed" || score.gameStatus === "inprogress")
return (
<Scores
className="scroll-box-item"
role="listitem"
key={index}
id={index}
gameDate={score.gameDate}
gameStatus={score.gameStatus === "closed" ? "Final" : "Live" }
homeTeam={score.homeTeam}
homePoints={score.homePoints}
awayPoints={score.awayPoints}
awayTeam={score.awayTeam}
/>
);
})}
</div>
</div>
</div>
</Stack>
<h3 className="home-header" style={{paddingTop: 10 }}>Upcoming Games</h3>
<hr className="lineRow" style={{ }}></hr>
<Grid container spacing={3} direction="row" justifyContent="flex-start">
{scores.map((score, index) => {
if(score.gameDate === date && score.gameStatus === "scheduled" || score.gameStatus === "upcoming")
return (
<Scores
className="scroll-box-item"
role="listitem"
key={index}
id={index}
time={score.gameDateandTime}
gameStatus={score.gameStatus === "scheduled" ? "Upcoming" : ""}
homeTeam={score.homeTeam}
homePoints={score.homePoints}
awayPoints={score.awayPoints}
awayTeam={score.awayTeam}
/>
);
})}
</Grid>
</div>
);
}
export default NHL;
How can I access the gameId from the NHL function and use that in my Score component?
I'm working on a website where users can read book. In this, homepage showcase multiple books but I want this page to change the size of book cover depending upon display size and not crop them.
I'm also using mui library for creating ui elements.
Home.js
import { ImageList, ImageListItem, ImageListItemBar,
Typography, Link, Grid, Divider } from '#mui/material'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { PageLayout, Context } from '../Components/PageLayout'
function Feed({ catg, catgTitle, routeName = "" }) {
const navg = useNavigate();
const bookCatg = useContext(Context)[0][catg];
return (
<>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Typography variant="h6">{catgTitle}</Typography>
</Grid>
<Grid item>
<Link
href={`/book/${routeName}`}
variant="body1"
underline="none"
component="button"
>
More..
</Link>
</Grid>
</Grid>
<Divider />
<ImageList
sx={{ pt: '10px' }}
cols={4}
spacing={1}
rowHeight={250}
>
{bookCatg.map((book) => (
<ImageListItem key={book._id}>
<img
onClick={() => navg(`/book/${book._id}`)}
src={book.img}
sx={{ height: "50px" }}
alt='Not Found'
loading="lazy"
/>
<ImageListItemBar
onClick={() => navg(`/book/${book._id}`)}
sx={{ width: '10vw' }}
title={book.title}
position="below"
/>
</ImageListItem>
))}
</ImageList>
</>
)
}
export default function Home(){
var element = <>
<Feed catgTitle={'New Arrival'}
catg={'newArrival'} routeName={'newest'} />
<Feed catgTitle={'Popular Books'}
catg={'popRating'} routeName={'popular'} />
<Feed catgTitle={'Hot Rated'}
catg={'hotRating'} routeName={'hot'} />
<Feed catgTitle={'Rank'}
catg={'ranking'} routeName={'ranking'} />
</>;
return (
<PageLayout
url="/book"
gridElem={element}
failureMsg="Error"
/>
)
}
PageLayout.jsx
import { Grid, Typography } from '#mui/material'
import { useState, useEffect, createContext } from 'react'
import axios from 'axios'
import Navbar from './Navbar'
import AppFormNav from './AppFormNav'
import Loading from './Loading'
export const Context = createContext({});
export function PageLayout({ url, nav='normal', elem, gridElem, failureMsg }){
const NavType = {
'normal': <Navbar />,
'form': <AppFormNav />
};
const [info, setInfo] = useState({});
const fetchData = async() => {
axios.get(url)
.then((resp) => {
if(resp.status === 200)
setInfo({status: 200, data: resp.data.data});
else if(resp.status === 204)
setInfo({ status: 204, data: resp.message });
})
.catch((err) => {
setInfo({ status: 400, data: err.response.data.data })
// console.log("Bad Request", err.resp.status);
})
};
useEffect(() => {
fetchData();
}, []);
var gridElement = <Loading />, element, message;
if(info.status === 200){
gridElement = gridElem;
element = elem;
}
else if (info.status === 204){
gridElement=<></>;
message = failureMsg ? failureMsg : info.data;
}
return (
<Context.Provider value={info?.data}>
<Grid container sx={{ justifyContent: 'center' }}>
<Grid item> { NavType[nav] } </Grid>
<Grid item xs={12}> { element } </Grid>
<Grid item xs={10}>
<Typography sx={{ justifyContent: "center", alignItems: "center" }}> {message} </Typography>
</Grid>
<Grid item xs={ 10 }> { gridElement } </Grid>
</Grid>
</Context.Provider>
)
}
current look
As you can see the bookcover of different size take up space of other book cover.
Home.jsx
import { ImageList, ImageListItem, ImageListItemBar,
Typography, Link, Grid, Divider } from '#mui/material'
import { useContext } from 'react'
import { useNavigate } from 'react-router-dom'
import { PageLayout, Context } from '../Components/PageLayout'
function Feed({ catg, catgTitle, routeName = "" }) {
const navg = useNavigate();
const bookCatg = useContext(Context)[0][catg];
return (
<>
<Grid
container
direction="row"
justifyContent="space-between"
alignItems="center"
>
<Grid item>
<Typography variant="h6">{catgTitle}</Typography>
</Grid>
<Grid item>
<Link
href={`/book/${routeName}`}
variant="body1"
underline="none"
component="button"
>
More..
</Link>
</Grid>
</Grid>
<Divider />
<ImageList
sx={{ pt: '10px' }}
cols={4}
spacing={1}
// rowHeight={250}
>
{bookCatg.map((book) => (
<ImageListItem key={book._id}>
<img
onClick={() => navg(`/book/${book._id}`)}
src={book.img}
//changed start from here
style={{ aspectRatio: "0.8", resize: "auto", objectFit: "scale-down" }}
//change end here
alt='Not Found'
loading="lazy"
/>
<ImageListItemBar
onClick={() => navg(`/book/${book._id}`)}
sx={{ width: '10vw' }}
title={book.title}
position="below"
/>
</ImageListItem>
))}
</ImageList>
</>
)
}
export default function Home(){
var element = <>
<Feed catgTitle={'New Arrival'}
catg={'newArrival'} routeName={'newest'} />
<Feed catgTitle={'Popular Books'}
catg={'popRating'} routeName={'popular'} />
<Feed catgTitle={'Hot Rated'}
catg={'hotRating'} routeName={'hot'} />
<Feed catgTitle={'Rank'}
catg={'ranking'} routeName={'ranking'} />
</>;
return (
<PageLayout
url="/book"
gridElem={element}
failureMsg="Error"
/>
)
}
just added this style to img tag
style={{ aspectRatio: "0.8", resize: "auto", objectFit: "scale-down"
}}
I want to save the progress state of Mobile Stepper, so that when user logged out, the user can resume from the place where he left. If progress bar is filled 2% then when next time the user login he should resume from 2% not from 0.
Here is the code:
import React, { useState, useReducer, useEffect } from "react";
import { Button, Typography, Grid, CircularProgress, Paper } from '#mui/material/';
import guardianOptions from '../../../constants/guardianOptions.js';
import studentClasses from '../../../constants/studentClasses.js';
import Stepper from '#mui/material/Stepper';
import Step from '#mui/material/Step';
import StepLabel from '#mui/material/StepLabel';
import MobileStepper from '#mui/material/MobileStepper';
import StepContent from '#mui/material/StepContent';
import { makeStyles, withStyles, createStyles } from '#mui/styles';
import { purple } from '#mui/material/colors';
import TextField from '#mui/material/TextField';
import useStyles from './styles';
import InputLabel from '#mui/material/InputLabel';
import MenuItem from '#mui/material/MenuItem';
import FormHelperText from '#mui/material/FormHelperText';
import FormControl from '#mui/material/FormControl';
import Select from '#mui/material/Select';
const User= (props) => {
const { activeStep: lastActiveStep, userId } = props;
const classes = useStyles();
const [guardianType, setGuardianType] = useState(0);
const [activeStep, setActiveStep] = useState(lastActiveStep || 0);
const [guardianRelationOptions, setGuardianRelationOptions] = useState(0);
const [guardianDetailsForm, setGuardianDetailsForm] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{}
);
const [studentDetailsForm, setStudentDetailsForm] = useReducer(
(state, newState) => ({ ...state, ...newState }),
{ s_firstName: '', s_lastName: '', s_age: '', s_class: '' }
);
React.useEffect(() => {
async function updateActiveStep() {
// this is just pseudo code - whatever your endpoint looks like
await window.fetch('http://localhost:8080/api/user/:userId', {
method: 'PUT',
body: JSON.stringify({activeStep})
})
}
updateActiveStep()
}, [activeStep]);
function getSteps(){
return [<b style={{color:'purple'}}>'Personal Details'</b>,
<b style={{color:'purple'}}>'Relation'</b>,
<b style={{color:'purple'}}>'Guardian Details'</b>];
}
const steps = getSteps();
function onGuardianTypeChangeChange(event) {
// setAge(event.target.value);
setGuardianType(event.target.value);
let _guardianRelationOptions = guardianOptions.find(options => options.value === event.target.value);
setGuardianRelationOptions(_guardianRelationOptions.relationships);
}
const handleGuardianDeatilsInput = evt => {
const name = evt.target.name;
const newValue = evt.target.value;
setGuardianDetailsForm({ [name]: newValue });
};
const handleGuardianDetailsSubmit = evt => {
evt.preventDefault();
let data = { guardianDetailsForm };
props.onGuardianDetails(guardianDetailsForm)
console.log(data + "new user");
// console.log( props.onGuardianDetails(guardianDetailsForm) + "gana bajao");
setActiveStep((prevActiveStep) => prevActiveStep+1);
setGuardianDetailsForm();
}
function getStepContent(step) {
switch (step) {
case 0: if (!props.user.s_firstName) {
return (<div>
<form id ="form-step0" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<TextField
id="outlined-basic"
name="s_firstName"
label="First Name"
variant="outlined"
defaultValue={guardianDetailsForm.s_firstName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-basic"
name="s_lastName"
label="Last Name"
variant="outlined"
defaultValue={guardianDetailsForm.s_lastName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-number"
label="Age"
name="s_age"
defaultValue={guardianDetailsForm.s_age}
type="number"
InputLabelProps={{
shrink: true,
}}
onChange={handleGuardianDeatilsInput}
variant="outlined"
/>
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Class</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
value={guardianDetailsForm.s_class}
onChange={handleGuardianDeatilsInput}
label="Class"
name="s_class"
>
{studentClasses.map(c =>
<MenuItem key={c.value} value={c.value}>{c.name}</MenuItem>
)}
</Select>
{/* <Button variant="contained" type="submit" color="primary" >NEXT</Button> */}
</FormControl>
</form>
</div> )}
;
case 1: if (!props.user.g_relationship) {
return ( <div>
<form id="form-step1" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Relationship</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
onChange={onGuardianTypeChangeChange}
label="Relationship"
>
{guardianOptions.map(type =>
<MenuItem key={type.value} value={type.value}>{type.name}</MenuItem>
)}
</Select>
</FormControl>
{guardianRelationOptions ?
<FormControl variant="outlined" className={classes.formControl}
sx={{ m: 1, minWidth: 120 }}>
<InputLabel id="demo-simple-select-outlined-label">Relation</InputLabel>
<Select
labelId="demo-simple-select-outlined-label"
id="demo-simple-select-outlined"
// value={age}
name="g_relationship"
value={guardianDetailsForm.g_relationship}
onChange={handleGuardianDeatilsInput}
label="Relation"
>
{guardianRelationOptions.map(type =>
<MenuItem key={type.value} value={type.value}>{type.name}</MenuItem>
)}
</Select>
</FormControl> : null
}
{!g_relationship} onClick={() => props.onGuardianDetails({g_relationship})}>NEXT</Button> */}
{/* <Button variant="contained" color="primary" type="submit">NEXT</Button> */}
</form>
</div> )}
;
case 2:
return ( <div>
<form id="form-step2" className={classes.root} onSubmit={handleGuardianDetailsSubmit} noValidate autoComplete="off">
<TextField
id="outlined-basic"
name="g_firstName"
label="First Name"
variant="outlined"
defaultValue={guardianDetailsForm.g_firstName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-basic"
name="g_lastName"
label="Last Name"
variant="outlined"
defaultValue={guardianDetailsForm.g_lastName}
onChange={handleGuardianDeatilsInput} />
<TextField
id="outlined-number"
label="Age"
name="g_age"
defaultValue={guardianDetailsForm.g_age}
type="number"
InputLabelProps={{
shrink: true,
}}
onChange={handleGuardianDeatilsInput}
variant="outlined"
/>
</form>
</div>)
;
default:
return 'welcome lets fill the progress.' ;
}
}
return (
<div className={classes.root} align="center">
<div className = {classes.actionsContainer}>
<Paper square elevation={0}>
<Typography>{getStepContent(activeStep)}</Typography>
</Paper>
<MobileStepper
variant="progress"
steps= {4}
position="bottom"
activeStep={activeStep}
sx={{ minWidth: 400, flexGrow: 1 }}
nextButton={
<>
<Button size="small" onClick={handleGuardianDetailsSubmit} type="submit"
form={`form-step${activeStep}`}>
{activeStep === steps.length-1? 'Finish' : 'Next'}
</Button>
</>
}
/>
</div>
</div>
);
}
export default User;
If user refreshes or reload the page, he should see the progress from where he has left.
#Tanya
So you are saying that your user can authorize. When this happens do you receive any data about the user? Can you update the user data via POST or PUT request? If so, I'd store the active step with this data.
Assuming you have some user data that you receive when the user signs in:
// user - user data
export default function BuildAdStepper({user}) {
const { activeStep: lastActiveStep, userId } = user
const classes = useStyles();
const theme = useTheme();
const [activeStep, setActiveStep] = React.useState(lastActiveStep || 0);
React.useEffect(() => {
async function updateActiveStep() {
// this is just pseudo code - whatever your endpoint looks like
await window.fetch('/yourEndpoint/:userId', {
method: 'PUT',
body: JSON.stringify({activeStep})
})
}
updateActiveStep()
}, [activeStep])
/* ... rest of your component * /
}
I have an array of football matches that I'll like users to enter their predictions. The issue I'm facing is that I'm unable to get save this form into an array.
Here is the visual look of the form.
Current form output
Here's my react code
import React, { useEffect, useState } from 'react';
import { Row, Col } from 'antd';
import moment from 'moment';
import { PredictTag, PotTitle, AmountInput } from '../../styles/styling';
import { Form, Container } from 'react-bootstrap';
import { produce } from "immer";
const StakeForm = (props) => {
const { potter } = props;
const [inputList, setInputList] = useState([
]);
const pots = potter.pot.findPot.pot;
useEffect(() => {
pots.slice(0, 4).map(() => {
setInputList(currentInput => [{
pot: {home_score: '', away_score: ''}
}
]);
})
},[]);
return (
<Container>
<Row justify="space-around" align="middle">
<Col span={12}><PotTitle>{potter.pot.findPot.pot_name}</PotTitle></Col>
<Col span={12} className="mt-2">
<PredictTag>Home</PredictTag>
<PredictTag>Away</PredictTag>
</Col>
</Row>
<Form>
{pots.map(({_id, home_team, away_team, date, time}) => (
<Row justify="space-around" align="middle" key={_id}>
<Col span={12}>
<h6>{home_team} vs {away_team}</h6>
<p>{moment(date).format("MMM Do, YYYY")} | {moment(time).format('LT')}</p>
</Col>
<Col span={12}>
{inputList.map((item, index) => (
<Container key={index}>
<Row>
<Col span={12}>
<Form.Group controlId="home" style={{ width: 100, }}>
<Form.Control as="select"
onChange={(e) => {
const home_score = e.target.value;
setInputList((currentInput) =>
produce(currentInput, (v) => {
v[index].pot.home_score = home_score;
})
)}}
>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Col>
<Col span={12}>
<Form.Group controlId="away" style={{ width: 100, float: 'left' }}>
<Form.Control as="select"
onChange={(e) => {
const away_score = e.target.value;
setInputList((currentInput) =>
produce(currentInput, (v) => {
v[index].pot.away_score = away_score;
})
)}}
>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
</Form.Control>
</Form.Group>
</Col>
</Row>
</Container>
))}
</Col>
</Row>
))}
<pre>{JSON.stringify(inputList, null, 2)}</pre>
</Form>
</Container>
);
};
export default StakeForm;
Currently, all home_score and away_score fields only return a single output rather than creating an object for each of the matches. Please do help me on this.
I am trying to redirect the user if he/she has not logged in. I am using the below snipet to do that.
if(!this.props.isAuthenticated){
this.nextPath('/account/login')
}
But when I execute it, I am getting an error. (Error: Maximum update depth exceeded). If I takeoff the above snippet. I am not getting Error.
I have also attached the full code. Help me with debugging this problem.
import React, { Component, Suspense } from 'react'
import { Layout, Menu } from 'antd';
import {TagFilled, SettingFilled , FileAddOutlined, DeleteFilled, LogoutOutlined } from '#ant-design/icons';
import { renderRoutes } from 'react-router-config';
import { loadLabels, deleteLabel} from '../../Store/Action/label'
import { logout } from '../../Store/Action/auth'
import { connect } from 'react-redux';
import Dialog from '#material-ui/core/Dialog';
import DialogActions from '#material-ui/core/DialogActions';
import DialogContent from '#material-ui/core/DialogContent';
import DialogTitle from '#material-ui/core/DialogTitle';
import Button from '#material-ui/core/Button';
import AddLabel from './Components/AddLabel'
import SideBar from './Components/SideBar'
import Avatar from '#material-ui/core/Avatar';
import {Dropdown} from 'react-bootstrap'
import Card from '#material-ui/core/Card';
import CardContent from '#material-ui/core/CardContent';
import { Row, Col, ListGroup } from 'react-bootstrap';
import Typography from '#material-ui/core/Typography';
import Divider from '#material-ui/core/Divider';
import ListSubheader from '#material-ui/core/ListSubheader';
import List from '#material-ui/core/List';
import ListItem from '#material-ui/core/ListItem';
import ListItemIcon from '#material-ui/core/ListItemIcon';
import ListItemText from '#material-ui/core/ListItemText';
import { Redirect } from 'react-router-dom';
const { Header, Content, Footer, Sider } = Layout;
export class HomePage extends Component {
//Constructor
constructor(){
super()
this.state = {
collapsed: false,
addLabelVisible: false,
isHovered: {},
anchorEl: null,
setAnchorEl: null
};
this.addLabelToggle = this.addLabelToggle.bind(this)
this.handleMouseEnter = this.handleMouseEnter.bind(this);
this.handleMouseLeave = this.handleMouseLeave.bind(this);
this.handlelogOut = this.handlelogOut.bind(this);
this.fetchData = this.fetchData.bind(this);
}
//Collapse the Sidebar
onCollapse = collapsed => {
this.setState({ collapsed });
};
//Toogle Add Label Dialog
addLabelToggle(addLabelVisible) {
this.setState({ addLabelVisible });
}
//Fetch All labels from the API
componentDidMount(){
console.log('problem here 1')
this.props.loadLabels()
}
//API CAll
fetchData(){
this.props.loadLabels()
}
//Called Whenever new Label is Added
componentDidUpdate(prevProps) {
if(typeof(prevProps.labels) === 'undefined' ) {
this.fetchData()
}
else if(prevProps.labels === this.props.labels){
this.fetchData()
}else{
}
}
//Hover
handleMouseEnter = index => {
this.setState(prevState => {
return { isHovered: { ...prevState.isHovered, [index]: true } };
});
};
//Hover
handleMouseLeave = index => {
this.setState(prevState => {
return { isHovered: { ...prevState.isHovered, [index]: false } };
});
};
//Delete a Label
handleLabelDelete(id){
const data = {
id: id,
img: 'https://zos.alipayobjects.com/rmsportal/ODTLcjxAfvqbxHnVXCYX.png'
}
this.props.deleteLabel(data)
}
handleClick = (event) => {
this.setState({
anchorEl: event.currentTarget
})
};
handleClose = () => {
this.setState({
anchorEl: null
})
};
handlelogOut = (e) =>{
console.log('logout')
this.props.logout()
}
nextPath(path) {
this.props.history.push(path);
}
render() {
const open = Boolean(this.anchorEl);
const id = open ? 'simple-popover' : undefined;
const { arr, isHovered } = this.state;
console.log(this.props.request)
if(!this.props.isAuthenticated){
this.nextPath('/account/login')
}
return (
<Layout style={{ minHeight: '100vh' }}>
{/* Navbar */}
<Header className="header" style={{ height: '60px', backgroundColor: '#1a243a' }}>
<div style={{ width: '120px', height: '31px', float: 'left', margin: '16px 28px 16px 0' }}>
<h2 style={{ color: 'white' }}>Nexdo</h2>
</div>
<div style={{ float: 'right' }}>
<Dropdown>
<Dropdown.Toggle style={{ backgroundColor: 'transparent', borderColor: 'transparent' }} id="dropdown-basic">
<Avatar>{this.props.user?this.props.user.first_name.slice(0,1):""}</Avatar>
</Dropdown.Toggle>
<Dropdown.Menu>
<Card style={{ background: '#ffffff', boxShadow: 'none', width: '280px' }}>
<CardContent>
<Row>
<Col xs={9}>
<Typography style={{ alignContent: 'right', justifyContent: 'right', display: 'flex' }} variant="h6" noWrap>
{this.props.user?this.props.user.first_name:""} {this.props.user?this.props.user.last_name:""}
</Typography>
<Typography style={{ color: '#333333', fontSize: '11px' , alignContent: 'right', justifyContent: 'right', display: 'flex' }} variant="h6" noWrap>
{this.props.user?this.props.user.email:""}
</Typography>
</Col>
<Col xs={3}>
<Avatar>{this.props.user?this.props.user.first_name.slice(0,1):""}</Avatar>
</Col>
</Row>
</CardContent>
<Divider />
<List
component="nav"
aria-labelledby="nested-list-subheader"
subheader={
<ListSubheader component="div" id="nested-list-subheader">
Actions
</ListSubheader>
}
>
<ListItem button>
<ListItemIcon>
<SettingFilled />
</ListItemIcon>
<ListItemText primary="Profile" />
</ListItem>
<a onClick={this.handlelogOut}>
<ListItem button>
<ListItemIcon>
<LogoutOutlined />
</ListItemIcon>
<ListItemText primary="Logout" />
</ListItem>
</a>
</List>
</Card>
</Dropdown.Menu>
</Dropdown>
</div>
</Header>
{/* SideBar and Content */}
<Layout className="site-layout">
<SideBar labelData = {this.props.labels} addLabelToggle = {this.addLabelToggle}/>
{/* Content which will be loaded dynamically */}
<Content style={{ margin: '0 16px' }}>
<main style={{ backgroundColor: "#fafafa", minHeight: '-webkit-fill-available' }} >
<Suspense fallback={<div>Loading...</div>}>
{renderRoutes(this.props.route.routes,)}
</Suspense>
</main>
</Content>
</Layout>
{/* Footer */}
<Footer style={{ textAlign: 'center' }}>Joan Louji ©2020 Created by Joan Louji</Footer>
{/* Add Label Dialog Box */}
<Dialog scroll="paper" open={this.state.addLabelVisible} onClose={()=>this.addLabelToggle(false)} aria-labelledby="scroll-dialog-title" aria-describedby="scroll-dialog-description">
<DialogTitle id="form-dialog-title"><b>Labels</b></DialogTitle>
<DialogContent>
<div style={{ height: "400px", width: "300px" }}>
{/* Display the Add Label Box */}
<AddLabel editClassName="form-control" value="Create a new Label" />
{this.props.labels?(this.props.labels.length==0?
<div style={{ margin: "0 auto", fontSize: '60px' ,display: 'flex' ,justifyContent: 'center', alignContent: 'center', alignItems: 'center', height: '100%', color: '#e0ebff'}}>
<div className="row">
<div className="col-12">
<FileAddOutlined style={{ margin: "0 auto", fontSize: '60px' ,display: 'flex' ,justifyContent: 'center', alignContent: 'center', alignItems: 'center', height: '100%', width: '100%' ,color: '#e0ebff'}}/>{"\n"}
</div>
<div className="col-12">
<h3 style={{ margin: "0 auto", fontSize: '20px' ,display: 'flex' ,justifyContent: 'center', alignContent: 'center', alignItems: 'center', height: '100%', width: '100%' ,color: '#e0ebff', marginTop: '20px'}}>Label Not Found</h3>
</div>
</div>
</div>
:
""
):"sd"}
{/* Render All the Labels in the Dialog Box */}
{this.props.labels?this.props.labels.map((data,index)=>{
return(
<div style={{ marginTop: '20px', }} >
<div key={data.name}>
<div className="row">
<div className="col-1" onMouseEnter={()=>this.handleMouseEnter(index)} onMouseLeave={()=>this.handleMouseLeave(index)}>
{this.state.isHovered[index]?
<DeleteFilled style={{ fontSize: 17, color: 'grey', cursor: 'pointer' }} onClick={(e)=>this.handleLabelDelete(data._id)}/>
:
<TagFilled style={{ fontSize: 17, color: 'grey' }}/>
}
</div>
<div className="col-9">
{data.name}
</div>
</div>
</div>
</div>
)
}):
<div>
<FileAddOutlined />
</div>
}
</div>
</DialogContent>
<DialogActions>
<Button onClick={(e)=>{
this.addLabelToggle(false)
}}>Done</Button>
</DialogActions>
</Dialog>
</Layout>
);
}
}
//mapStateToProps
const mapStateToProps = (state) => ({
isLoading: state.labels.isLoading,
user: state.auth.user,
labels: state.labels.labels,
});
export default connect(mapStateToProps, {loadLabels, deleteLabel, logout})(HomePage)
From the error, it is clear that there "IF" condition you have mentioned in the ComponentDidUpdate is calling setState repeatedly. That's why the react framework limits.
Please refer Component Did Update