While creating a navbar I used multiple collapse components from MUI in order to create generate the menu items.
Its not a code breaking error but an annoying to have one. I get the following error in my console.
Warning: Encountered two children with the same key, `indexCollapseListItem`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
List#http://localhost:3000/static/js/bundle.js:18632:82
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Transition#http://localhost:3000/static/js/bundle.js:102860:30
Collapse#http://localhost:3000/static/js/bundle.js:12704:82
nav
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
List#http://localhost:3000/static/js/bundle.js:18632:82
div
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Paper#http://localhost:3000/static/js/bundle.js:22524:82
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Drawer#http://localhost:3000/static/js/bundle.js:13680:83
nav
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Box#http://localhost:3000/static/js/bundle.js:29040:72
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Box#http://localhost:3000/static/js/bundle.js:29040:72
NavBar#http://localhost:3000/static/js/bundle.js:1766:7
Dashboard
Routes#http://localhost:3000/static/js/bundle.js:101977:7
Router#http://localhost:3000/static/js/bundle.js:101914:7
BrowserRouter#http://localhost:3000/static/js/bundle.js:101391:7
App
Provider#http://localhost:3000/static/js/bundle.js:98572:15 react-dom.development.js:67
React 19
js index.js:8
factory react refresh:6
Webpack 3
Warning: Encountered two children with the same key, `indexCollapseListItem`. Keys should be unique so that components maintain their identity across updates. Non-unique keys may cause children to be duplicated and/or omitted — the behavior is unsupported and could change in a future version.
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
List#http://localhost:3000/static/js/bundle.js:18632:82
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Transition#http://localhost:3000/static/js/bundle.js:102860:30
Collapse#http://localhost:3000/static/js/bundle.js:12704:82
nav
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
List#http://localhost:3000/static/js/bundle.js:18632:82
div
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Paper#http://localhost:3000/static/js/bundle.js:22524:82
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Transition#http://localhost:3000/static/js/bundle.js:102860:30
Slide#http://localhost:3000/static/js/bundle.js:24602:7
Unstable_TrapFocus#http://localhost:3000/static/js/bundle.js:8464:7
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Portal#http://localhost:3000/static/js/bundle.js:8032:7
ModalUnstyled#http://localhost:3000/static/js/bundle.js:7245:7
Modal#http://localhost:3000/static/js/bundle.js:21375:82
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Drawer#http://localhost:3000/static/js/bundle.js:13680:83
nav
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Box#http://localhost:3000/static/js/bundle.js:29040:72
div
./node_modules/#emotion/react/dist/emotion-element-699e6908.browser.esm.js/withEmotionCache/<#http://localhost:3000/static/js/bundle.js:5079:66
Box#http://localhost:3000/static/js/bundle.js:29040:72
NavBar#http://localhost:3000/static/js/bundle.js:1766:7
Dashboard
Routes#http://localhost:3000/static/js/bundle.js:101977:7
Router#http://localhost:3000/static/js/bundle.js:101914:7
BrowserRouter#http://localhost:3000/static/js/bundle.js:101391:7
App
Provider#http://localhost:3000/static/js/bundle.js:98572:15 react-dom.development.js:67
I am not setting this key in my code but why is it being set by default, which component is being assigned the same key again and again and how can I fix this.
My navbar code is the following for your reference.
//ASSETS
import AuraLogo from '../../../assets/images/logo/aura.png'
//REACT
import { useState } from 'react'
import { Link } from 'react-router-dom'
//MenuItems
import menuItems from '../dashboard/menu-items/index'
//MUI
import PropTypes from 'prop-types'
import AppBar from '#mui/material/AppBar'
import Box from '#mui/material/Box'
import CssBaseline from '#mui/material/CssBaseline'
import Divider from '#mui/material/Divider'
import Drawer from '#mui/material/Drawer'
import IconButton from '#mui/material/IconButton'
import List from '#mui/material/List'
import ListItemIcon from '#mui/material/ListItemIcon'
import ListItemText from '#mui/material/ListItemText'
import MenuIcon from '#mui/icons-material/Menu'
import Toolbar from '#mui/material/Toolbar'
import Typography from '#mui/material/Typography'
import Collapse from '#mui/material/Collapse'
import ListSubheader from '#mui/material/ListSubheader'
import ListItemButton from '#mui/material/ListItemButton'
import ExpandLess from '#mui/icons-material/ExpandLess'
import ExpandMore from '#mui/icons-material/ExpandMore'
import ArrowBackIosNew from '#mui/icons-material/ArrowBackIosNew'
const drawerWidth = 240
// TODO - fix duplicate keys error in components from MUI
function NavBar(props) {
const { window } = props
const [mobileOpen, setMobileOpen] = useState(false)
// TODO - change appbar title depending on the category clicked
const [title, setTitle] = useState()
const [open, setOpen] = useState({
dashboard: true,
categories: true,
subcategories: true,
products: true,
auth: true,
other: false,
})
const handleClick = (id) => {
setOpen((prevState) => ({ ...prevState, [id]: !prevState[id] }))
}
// const handleClick = (id) => {
// setOpen({ ...state, [id]: !open[id] });
// // setOpen((prevState => ({...prevState, [id]: !prevState[id]}))
// };
// const handleClick = (item) => {
// setOpen({
// item : !open
// })
// }
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen)
}
const drawer = (
<div>
<Toolbar key={'LogoToolbar'}>
<img
as={Link}
src={AuraLogo}
style={{
maxWidth: '60%',
maxHeight: '60%',
paddingTop: '10px',
paddingBottom: '10px',
margin: '0 auto',
}}
/>
</Toolbar>
<Divider />
{menuItems.items.map(({id, icon, title, children}) => (
<>
<List
key={id+'ParentList'}
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
component='nav'
aria-labelledby='nested-list-subheader'
// subheader={
// <ListSubheader component='div' id='nested-list-subheader'>
// {item.subheader}
// </ListSubheader>
// }
>
<ListItemButton key={id+'listitembutton'} onClick={() => handleClick(id)}>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={title} />
{open[id] ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse key={id+'collapse'} in={open[id]} timeout='auto' unmountOnExit>
<List key={id+'listchildren'} component='div' disablePadding>
{children.map(({id, icon, title, url}) => (
<ListItemButton
component={Link}
to={`${url}`}
onClick={handleDrawerToggle}
key={id+'CollapseListItem'}
sx={{ pl: 3 }}>
<ListItemIcon>{icon}</ListItemIcon>
<ListItemText primary={title} />
</ListItemButton>
))}
</List>
</Collapse>
</List>
<Divider />
</>
))}
<Link style={{textDecoration: 'none', color: '#202020'}} to='/'>
<ListItemButton key={'returnlistitembutton'}>
<ListItemIcon><ArrowBackIosNew/></ListItemIcon>
<ListItemText primary='Πίσω στους καταλόγους' />
</ListItemButton>
</Link>
</div>
)
const container =
window !== undefined ? () => window().document.body : undefined
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar
position='fixed'
sx={{
width: { sm: `calc(100% - ${drawerWidth}px)` },
ml: { sm: `${drawerWidth}px` },
}}>
<Toolbar>
<IconButton
color='inherit'
aria-label='open drawer'
edge='start'
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: 'none' } }}>
<MenuIcon />
</IconButton>
<Typography variant='h6' noWrap component='div'>
Dashboard
{/* TODO - add more functionallity to appbar */}
</Typography>
</Toolbar>
</AppBar>
<Box
component='nav'
sx={{ width: { sm: drawerWidth }, flexShrink: { sm: 0 } }}
aria-label='mailbox folders'>
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Drawer
key={'drawer'}
container={container}
variant='temporary'
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: 'block', sm: 'none' },
'& .MuiDrawer-paper': {
boxSizing: 'border-box',
width: drawerWidth,
},
}}>
{drawer}
</Drawer>
<Drawer
variant='permanent'
sx={{
display: { xs: 'none', sm: 'block' },
'& .MuiDrawer-paper': {
boxSizing: 'border-box',
width: drawerWidth,
},
}}
open>
{drawer}
</Drawer>
</Box>
<Box
component='main'
sx={{
flexGrow: 1,
p: 3,
width: { sm: `calc(100% - ${drawerWidth}px)` },
}}>
<Toolbar />
{props.children}
</Box>
</Box>
)
}
NavBar.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
window: PropTypes.func,
}
export default NavBar
The random key properties that appear almost everywhere was my quest in finding which component generates this error but with no luck.
As always thanks in advance and if by any chance you have any suggestions or solutions... I'm all ears!
You can try something like this.
<List
sx={{ width: '100%', maxWidth: 360, bgcolor: 'background.paper' }}
component='nav'
aria-labelledby='nested-list-subheader'
>
{menuItems.items.map(({id, icon, title, children}, idx) => (
<ListItemButton
onClick={() => handleClick(id)}
key={idx}
>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText
primary={title}
/>
{open[id] ? <ExpandLess /> : <ExpandMore />}
</ListItemButton>
<Collapse in={open[id]} timeout='auto' unmountOnExit>
<List component='div' disablePadding>
{children.map(({id, icon, title, url}, subidx) => (
<ListItemButton
component={Link}
to={`${url}`}
onClick={handleDrawerToggle}
key={subidx}
sx={{ pl: 3 }}
>
<ListItemIcon>
{icon}
</ListItemIcon>
<ListItemText
primary={title}
/>
</ListItemButton>
))}
</List>
</Collapse>
))}
</List>
Here :
{menuItems.items.map(({id, icon, title, children}) => (
<>
<List
key={id+'ParentList'}
Your key must be provide on the first child, in your case the React Fragment so :
{menuItems.items.map(({id, icon, title, children}) => (
<Fragment key={id+'ParentList'}>
<List ...
You need to put a uniq key, the index from map can provide the same issue if you use 2 times (0, 1, 2, 3 === 0, 1, 2, 3), you can use an add string or you can use a methods to generate an uniq key :
uuid nom package
nanoid from redux-toolkit (perfect if you use Redux, don't install it just for that)
create a method from Math.random like Math.floor(Math.random()*10000000).toString(16) ( => hexa key)
create a method from a prefix you give in method params and adding an element from a new Date() or timestamp
...
With this, you sure to create a new and uniq key.
With time, in order, I prefer Nanoid or Math random.
Related
I am trying to build a dashboard app with basic AppBar and a drawer based on this Demo
https://codesandbox.io/s/nj3u0q?file=/demo.tsx
But in this Demo, AppBar and Drawer and the Main content are all in a single page.
But I made it as separate components and did the layout like this
RootContainer.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import Box from '#mui/material/Box';
import CssBaseline from '#mui/material/CssBaseline';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Drawer from './Drawer';
import AppBar from './AppBar';
import Main from './MainContent';
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
return (
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar />
<Drawer />
<Main />
</Box>
);
}
Appbar.tsx
import * as React from 'react';
import { styled, useTheme } from '#mui/material/styles';
import MuiAppBar, { AppBarProps as MuiAppBarProps } from '#mui/material/AppBar';
import Toolbar from '#mui/material/Toolbar';
import Typography from '#mui/material/Typography';
import IconButton from '#mui/material/IconButton';
import MenuIcon from '#mui/icons-material/Menu';
const drawerWidth = 240;
interface AppBarProps extends MuiAppBarProps {
open?: boolean;
}
const AppBar = styled(MuiAppBar, {
shouldForwardProp: (prop) => prop !== 'open',
})<AppBarProps>(({ theme, open }) => ({
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.sharp,
duration: theme.transitions.duration.leavingScreen,
}),
...(open && {
width: `calc(100% - ${drawerWidth}px)`,
marginLeft: `${drawerWidth}px`,
transition: theme.transitions.create(['margin', 'width'], {
easing: theme.transitions.easing.easeOut,
duration: theme.transitions.duration.enteringScreen,
}),
}),
}));
export default function AppBar() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
return (
<AppBar position="fixed" style={{background:'#002a5e'}} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: 'none' }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
But the problem is when I click the sandwich button on the appbar, it reduces its width to show the Drawer, but the drawer not at all showing.
Drawer.tsx
import * as React from "react";
import { styled, useTheme } from "#mui/material/styles";
import Drawer from "#mui/material/Drawer";
import List from "#mui/material/List";
import Divider from "#mui/material/Divider";
import IconButton from "#mui/material/IconButton";
import ChevronLeftIcon from "#mui/icons-material/ChevronLeft";
import ChevronRightIcon from "#mui/icons-material/ChevronRight";
import ListItem from "#mui/material/ListItem";
import ListItemButton from "#mui/material/ListItemButton";
import ListItemIcon from "#mui/material/ListItemIcon";
import ListItemText from "#mui/material/ListItemText";
import InboxIcon from "#mui/icons-material/MoveToInbox";
import MailIcon from "#mui/icons-material/Mail";
const drawerWidth = 240;
const DrawerHeader = styled("div")(({ theme }) => ({
display: "flex",
alignItems: "center",
padding: theme.spacing(0, 1),
// necessary for content to be below app bar
...theme.mixins.toolbar,
justifyContent: "flex-end",
}));
export default function Drawer() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box",
},
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
I guess this problem is because the open constant is not getting updated into the drawer component. I am pretty new or only fews days of experience in react. Please help how can share these click event and constant in appbar to the drawer
It looks like with the separation, each component is having their own open state and handling functions, therefore the behavior is not shared.
Try pass these as props from the parent to communicate with children components.
React document about: Passing Props to a Component
There seems to be already a open state in the parent component RootContainer, so perhaps this state and handlers can be passed as props to the components, such as below example.
Over simplified example from forked demo: codesandbox
Define state and handler in the parent component, and pass these down to the children:
RootContainer.tsx
export default function PersistentDrawerLeft() {
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
return (
<Box sx={{ display: "flex" }}>
<CssBaseline />
<AppBar open={open} onDrawerOpen={handleDrawerOpen} />
<Drawer open={open} onDrawerClose={handleDrawerClose} />
<Main />
</Box>
);
}
Have each child component use the open state and handlers from the props, instead of their own, so that they share the same state:
Appbar.tsx
export default function AppBar({ open, onDrawerOpen }) {
const theme = useTheme();
return (
<AppBar position="fixed" style={{ background: "#002a5e" }} open={open}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={onDrawerOpen}
edge="start"
sx={{ mr: 2, ...(open && { display: "none" }) }}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap component="div">
Test
</Typography>
</Toolbar>
</AppBar>
);
}
Drawer.tsx
export default function Drawer({ open, onDrawerClose }) {
const theme = useTheme();
return (
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
"& .MuiDrawer-paper": {
width: drawerWidth,
boxSizing: "border-box"
}
}}
variant="persistent"
anchor="left"
open={open}
>
<DrawerHeader>
<IconButton onClick={onDrawerClose}>
{theme.direction === "ltr" ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{["Manage Recipe", "Reports", "Settings"].map((text, index) => (
<ListItem key={text} disablePadding>
<ListItemButton>
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
);
}
Hope this will help.
I am very new to Next.js, and am trying to create a basic app where we have a header, and the header has 3 buttons clicking which the users should be redirected to the required route. The header should be present in all the pages, hence have put it in _app.js itself. Clicking on either of the 3 buttons does show a change in URL, but the component doesn't get rendered, and I am struggling to understand why that's happening. All the 3 files which we need to route to have been created under the 'pages' folder, and have their names set as expected.
The _app.js:
import Head from "next/head";
import Header from "./components/header";
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>GameZop Assignment</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
</>
);
}
export default MyApp;
The header.js(from MUI):
const drawerWidth = 240;
const navItems = [
{ value: "users", label: "Users" },
{ value: "news", label: "News" },
{ value: "topUsers", label: "Top Users" },
];
function Header(props) {
const { window } = props;
const router = useRouter();
const [mobileOpen, setMobileOpen] = React.useState(false);
const showComponent = (comp) => {
router.push(`/${comp}`)
}
const handleDrawerToggle = () => {
setMobileOpen(!mobileOpen);
};
const drawer = (
<Box onClick={handleDrawerToggle} sx={{ textAlign: "center" }}>
<Typography variant="h6" sx={{ my: 2 }}>
MUI
</Typography>
<Divider />
<List>
{navItems.map((item) => (
<ListItem key={item.value} disablePadding>
<ListItemButton sx={{ textAlign: "center" }}>
<ListItemText primary={item.label} />
</ListItemButton>
</ListItem>
))}
</List>
</Box>
);
const container =
window !== undefined ? () => window().document.body : undefined;
return (
<Box sx={{ display: "flex" }}>
<AppBar component="nav">
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
sx={{ mr: 2, display: { sm: "none" } }}
>
<MenuIcon />
</IconButton>
<Typography
variant="h6"
component="div"
sx={{ flexGrow: 1, display: { xs: "none", sm: "block" } }}
>
Assignment
</Typography>
<Box sx={{ display: { xs: "none", sm: "block" } }}>
{navItems.map((item) => (
<Button
key={item}
sx={{ color: "#fff" }}
onClick={() => showComponent(item.value)}
>
{item.label}
</Button>
))}
</Box>
</Toolbar>
</AppBar>
<Box component="nav">
<Drawer
container={container}
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
sx={{
display: { xs: "block", sm: "none" },
"& .MuiDrawer-paper": {
boxSizing: "border-box",
width: drawerWidth,
},
}}
>
{drawer}
</Drawer>
</Box>
</Box>
);
}
Header.propTypes = {
window: PropTypes.func,
};
export default Header;
Finally, the users.js, which I want to load below this header:
function Users(){
return (
<>
<h2>Hello from users</h2>
</>
)
}
export default Users;
I found the mistake, I had removed the rendering of 'Component' from _app.js, hence the components weren't loading. The _app.js now becomes:
import Head from "next/head";
import Header from "./components/header";
function MyApp({ Component, pageProps }) {
return (
<>
<Head>
<title>GameZop Assignment</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<Header />
<Component {...pageProps} />
</>
);
}
export default MyApp;
Trying to render a component conditionally. I have a drawHelper variable & a function to toggle it true or false. The component renders or not based on the initial value of drawHelper. (false, doesn't render, true it does).
The toggle function changes the value. I checked with console.log(drawHelper) But changing the value does not make the component appear or disappear.
Am I missing something here?
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
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 DashboardIcon from '#material-ui/icons/Dashboard';
import MenuIcon from '#material-ui/icons/Menu';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme, Theme, createStyles } from '#material-ui/core/styles';
import { User } from './User';
import { Draw } from "components/Layout/Countryballs/Draw";
const drawerWidth = 240;
export const useStyles = makeStyles((theme: Theme) =>
createStyles({
root: {
display: 'flex',
color: '#fff',
},
drawer: {
[theme.breakpoints.up('sm')]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
marginLeft: drawerWidth,
[theme.breakpoints.up('sm')]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
menuButton: {
marginRight: theme.spacing(2),
[theme.breakpoints.up('sm')]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
backgroundColor: theme.palette.primary.main
},
content: {
flexGrow: 1,
},
menuItem: {
color: '#fff',
},
}),
);
export const Layout: React.FC<LayoutProps> = (props) => {
const { container } = props;
const classes = useStyles();
const theme = useTheme();
const [mobileOpen, setMobileOpen] = React.useState(false);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
// Display Draw component
// 1 Create property
var drawHelper: Boolean = false;
function toggleDraw() {
console.log(drawHelper);
drawHelper = !drawHelper;
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider />
<List>
{['Draw'].map((text) => (
<ListItem button key={text} onClick={toggleDraw} className={classes.menuItem}>
<ListItemIcon className={classes.menuItem}><DashboardIcon /></ListItemIcon>
<ListItemText primary={text} />
</ListItem>
))}
</List>
</div>
);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<Typography variant="h6" noWrap>
Project name
</Typography>
<User/>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer // this one is for mobile
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer // This one is for desktop
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
{/* This is where my components renders */}
{
drawHelper === true && (<Draw/>)
}
<div className={classes.toolbar} />
{props.children}
</main>
</div>
);
}
The variable drawHelper in your code is instantiated on every render. You'd want to use React's state to make sure your drawHelper's value is preserved on the next re-renders.
const [drawHelper, toggleDrawHelper] = React.useState(false)
function toggleDraw() {
toggleDrawHelper(!drawHelper);
}
Making a Drawer in React.Js with Material-UI.
When I switch to the mobile view, the Drawer content automatically shifts to the right side with a lot of margin.
It works fine the in desktop view.
I am not able to find the error in this code.
Could anyone guide me how can I solve this issue?
The code for the drawer:
import React from 'react';
[import PropTypes from 'prop-types';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Divider from '#material-ui/core/Divider';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import List from '#material-ui/core/List';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, useTheme } from '#material-ui/core/styles';
import { ListItem } from '#material-ui/core';
import { Data } from './data';
const drawerWidth = 240;
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
drawer: {
\[theme.breakpoints.up('sm')\]: {
width: drawerWidth,
flexShrink: 0,
},
},
appBar: {
marginLeft: drawerWidth,
\[theme.breakpoints.up('sm')\]: {
width: `calc(100% - ${drawerWidth}px)`,
},
},
menuButton: {
marginRight: theme.spacing(2),
\[theme.breakpoints.up('sm')\]: {
display: 'none',
},
},
toolbar: theme.mixins.toolbar,
drawerPaper: {
width: drawerWidth,
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
}));
function ResponsiveDrawer(props) {
const { container } = props;
const classes = useStyles();
const theme = useTheme();
const \[mobileOpen, setMobileOpen\] = React.useState(false);
var \[index, changeindex\] = React.useState(1);
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
const drawer = (
<div>
<div className={classes.toolbar} />
<Divider></Divider>
<List>
<ListItem button primary full large onClick = {()=>changeindex(index = 1)} > <Typography variant="h6" noWrap>
Test 1
</Typography></ListItem>
<ListItem button primary full large onClick={() => changeindex(index = 2)} >
<Typography variant="h6" noWrap>
Test2
</Typography>
</ListItem>
<ListItem button primary full large onClick={() => changeindex(index = 3)} >
<Typography variant="h6" noWrap>
Test3
</Typography>
</ListItem>
</List>
</div>
);
console.log(props);
return (
<div className={classes.root}>
<CssBaseline />
<AppBar position="fixed" className={classes.appBar}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<i class="material-icons">view_headline</i>
</IconButton>
<Typography variant="h6" noWrap>
Responsive drawer
</Typography>
</Toolbar>
</AppBar>
<nav className={classes.drawer} aria-label="mailbox folders">
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
container={container}
variant="temporary"
anchor={theme.direction === 'rtl' ? 'right' : 'left'}
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
</nav>
<main className={classes.content}>
<div className={classes.toolbar} />
{/* {console.log(index)} */}
<Data index={index} ></Data>
</main>
</div>
);
}
ResponsiveDrawer.propTypes = {
/**
* Injected by the documentation to work in an iframe.
* You won't need it on your project.
*/
container: PropTypes.instanceOf(typeof Element === 'undefined' ? Object : Element),
};
export default ResponsiveDrawer;][1]
Example image
I am using Material UI tabs and React-Router, both are working fine visually but I realized while using Developer tools that there is an error every time I click on a tab or on the menu button in the case of phone size. The error says:
index.js:1375 Warning: Material-UI: the value provided/to the Tabs component is invalid. None of the Tabs children have this value. You can provide one of the following values: 0, 1, 2, 3, 4, 5.
I tried replacing the router with a state and active index structure but the error still persists. How to make sure that the error doesn't occur when clicking on the tab or the menu button? And how to make sure it keeps directing us to the concerned page smoothly with the concerned path? Thanks
PS: the screens they are directed to are all texts, for now, nothing in particular, I can edit the post if showing the code is required, though I doubt so. Thanks again
import React from 'react';
import AppBar from '#material-ui/core/AppBar';
import CssBaseline from '#material-ui/core/CssBaseline';
import Drawer from '#material-ui/core/Drawer';
import Hidden from '#material-ui/core/Hidden';
import IconButton from '#material-ui/core/IconButton';
import MenuIcon from '#material-ui/icons/Menu';
import Toolbar from '#material-ui/core/Toolbar';
import Typography from '#material-ui/core/Typography';
import { makeStyles, withStyles } from '#material-ui/core/styles';
import SearchBar from '../TopBar/SearchBar'
import Home from '../Screens/Home'
import home from '../home.svg';
import Contact from '../Screens/Contact'
import contact from '../contact.svg';
import Profile from '../Screens/Profile'
import profile from '../profile.svg';
import Settings from '../Screens/Settings'
import settings from '../settings.svg'
import Tabs from "#material-ui/core/Tabs";
import Tab from "#material-ui/core/Tab";
import { Switch, Route, Link, BrowserRouter} from "react-router-dom";
const VerticalTabs = withStyles(theme => ({
flexContainer: {
flexDirection: "column"
},
indicator: {
display: "red"
},
root:{
position:"fixed",
left:-70,
top:0,
},
}))(Tabs);
const MyTab = withStyles(theme => ({
selected: {
color: "white",
borderRight: "none",
},
root: {
//minWidth: 121,
margin:0,
paddingBottom:119
},
'#media screen and (min-width: 600px) and (max-width: 1205px)':{
root: {
minWidth: 151,
}
}
}))(Tab);
const styles = theme => ({
root: {
flexGrow: 1,
marginTop: theme.spacing(3),
backgroundColor: theme.palette.background.paper,
},
tabRoot: {
minWidth: 10,
},
});
const useStyles = makeStyles(theme => ({
root: {
display: 'flex',
},
menuButton:{
visibility: "hidden"
},
appBar: {
marginLeft: 300,
},
drawerPaper: {
width: 100,
background: "#262A2C",
fontSize:65,
height:"120%",
top:-10
},
content: {
flexGrow: 1,
padding: theme.spacing(3),
},
'#media screen and (min-width: 600px) and (max-width: 1205px)':{
drawerPaper: {
width: 80,
//background: "black"
},
},
'#media (max-width: 600px)':{
drawerPaper: {
width: 300,
//background: "black"
},
appBar: {
},
menuButton:{
visibility: "visible"
},
}
}));
function ResponsiveDrawer() {
const [value, setValue] = React.useState(0);
const classes = useStyles();
const [mobileOpen, setMobileOpen] = React.useState(false);
function handleChange(event, Value) {
setValue(Value);
}
function handleDrawerToggle() {
setMobileOpen(!mobileOpen);
}
const drawer = (
<Route
path="/"
render={({ location }) => (
<nav>
<div style={{ left: 70, position: "relative", marginTop: 40 }}>
<VerticalTabs value={location.pathname} variant="fullWidth" onChange={handleChange}
>
<MyTab
component={Link} to="/"
icon ={<img
className= "home"
src={home}
alt="home"
/*Pay FlatIcon or replace by design *//>}
label={<p className="home-Text" >
Home
</p>}
/>
<MyTab
component={Link} to="/Screens/Contact"
icon ={<img
className= "contact"
src={contact}
alt="contact"
/*Pay FlatIcon or replace by design *//>}
label={<p className="contacts-Text" >
Contact
</p>}
/>
<MyTab
component={Link} to="/Screens/Profile"
icon={<img
className= "profile"
src={profile}
alt="profile"
/*Pay FlatIcon or replace by design *//>}
label={<p className= "profile-Text" >
Profile
</p>}
/>
<MyTab
component={Link} to="/Screens/Settings"
icon = {<img
className= "settings"
src={settings}
alt="settings"
/*Pay FlatIcon or replace by design *//>}
label={<p className="settings-Text" >
Settings
</p>}
/>
</VerticalTabs>
</div>
</nav>
)}
/>
);
return (
<nav>
<BrowserRouter>
<div className="aBar">
<CssBaseline />
<AppBar style={{position:"relative",background: 'transparent', boxShadow: 'none', color: "red"}}>
<Toolbar>
<IconButton
color="inherit"
aria-label="Open drawer"
edge="start"
onClick={handleDrawerToggle}
className={classes.menuButton}
>
<MenuIcon />
</IconButton>
<SearchBar />
<div className="logo">
<Typography
component={Link}
to="/"
className= "logo-Spec"
style={{fontSize:30, textDecoration: 'none' }}
variant="h1"
//don't forget variant= "h1" if you want to modify logo style
>
Logo
</Typography>
</div>
</Toolbar>
</AppBar>
<nav className={classes.drawer}>
{/* The implementation can be swapped with js to avoid SEO duplication of links. */}
<Hidden smUp implementation="css">
<Drawer
style={{color:"black"}}
variant="temporary"
open={mobileOpen}
onClose={handleDrawerToggle}
classes={{
paper: classes.drawerPaper,
}}
ModalProps={{
keepMounted: true, // Better open performance on mobile.
}}
>
{drawer}
</Drawer>
</Hidden>
<Hidden xsDown implementation="css">
<Drawer
classes={{
paper: classes.drawerPaper,
}}
variant="permanent"
open
>
{drawer}
</Drawer>
</Hidden>
<Switch>
<Route path="/Screens/Contact" component={Contact} />
<Route path="/Screens/Settings" component={Settings} />
<Route path="/Screens/Profile" component={Profile} />
<Route path="/" component={Home} />
</Switch>
</nav>
</div>
</BrowserRouter>
</nav>
);
}
export default withStyles(styles)(ResponsiveDrawer);
On your Tabs element you have a value of location.pathname. This indicates that the "selected" Tab is the one with this value, but you aren’t specifying a value prop for any of your tabs. When no value is specified, the value of a Tab defaults to its index within the Tabs parent.
The error message is telling you that the value on Tabs (via VerticalTabs) does not match any of the values on your individual Tab elements (MyTab). To resolve this, you should specify the value in the to property also as the value property on the Tab elements.
References:
https://material-ui.com/api/tabs/
https://material-ui.com/api/tab/