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;
Related
I'm trying to figure out how to use the Chakra UI page shell, so that I can add components to render in the main segment of the shell, for each sidebar menu item.
The sidebar menu has NavButton items as follows:
<NavButton label="Settings" />
I'm trying to find a way to add a component (say, Settings) which has the content to render in the main segment of the shell.
How can I do that?
The full dashboard is as follows:
import {
// As,
Avatar,
Box,
BoxProps,
Button,
ButtonProps,
chakra,
Container,
Divider,
Drawer,
DrawerContent,
DrawerOverlay,
Flex,
Heading,
HStack,
Icon,
Input,
InputLeftElement,
InputGroup,
// Progress,
// SimpleGrid,
Stack,
Text,
useBreakpointValue,
useColorModeValue,
useDisclosure,
IconButton,
IconButtonProps
} from '#chakra-ui/react'
import * as React from 'react'
import { FiDownloadCloud } from 'react-icons/fi'
// import { CgWorkAlt } from 'react-icons/cg'
import {
// FiBarChart2,
// FiBookmark,
// FiCheckSquare,
// FiHelpCircle,
// FiHome,
FiSearch,
// FiSettings
} from 'react-icons/fi'
const Card = (props: BoxProps) => (
<Box
minH="3xs"
bg="bg-surface"
boxShadow={useColorModeValue('sm', 'sm-dark')}
borderRadius="lg"
{...props}
/>
)
const Bar = chakra('span', {
baseStyle: {
display: 'block',
pos: 'absolute',
w: '1.25rem',
h: '0.125rem',
rounded: 'full',
bg: 'currentcolor',
mx: 'auto',
insetStart: '0.125rem',
transition: 'all 0.12s',
},
})
const ToggleIcon = (props: { active: boolean }) => {
const { active } = props
return (
<Box
className="group"
data-active={active ? '' : undefined}
as="span"
display="block"
w="1.5rem"
h="1.5rem"
pos="relative"
aria-hidden
pointerEvents="none"
>
<Bar top="0.4375rem" _groupActive={{ top: '0.6875rem', transform: 'rotate(45deg)' }} />
<Bar bottom="0.4375rem" _groupActive={{ bottom: '0.6875rem', transform: 'rotate(-45deg)' }} />
</Box>
)
}
interface ToggleButtonProps extends IconButtonProps {
isOpen: boolean
}
const ToggleButton = (props: ToggleButtonProps) => {
const { isOpen, ...iconButtonProps } = props
return (
<IconButton
position="relative"
variant="unstyled"
size="sm"
color={isOpen ? 'white' : 'muted'}
zIndex="skipLink"
icon={<ToggleIcon active={isOpen} />}
{...iconButtonProps}
/>
)
}
interface UserProfileProps {
name: string
// image: string
title: string
}
const UserProfile = (props: UserProfileProps) => {
const { name, title } = props
return (
<HStack spacing="3" ps="2">
<Avatar name={name} boxSize="10" />
<Box>
<Text fontWeight="medium" fontSize="sm">
{name}
</Text>
<Text color="muted" fontSize="sm">
{title}
</Text>
</Box>
</HStack>
)
}
const Sidebar = () => (
<Flex as="section" minH="100vh" bg="bg-canvas" maxW="100%" p="0">
<Flex
flex="1"
bg="brand.white"
// overflowY="auto"
maxW="100%" p="0"
// maxW={{ base: 'full', sm: 'xs' }}
py={{ base: '6', sm: '8' }}
pl={4}
pr={6}
mr={4}
// px={{ base: '4', sm: '6' }}
>
<Stack justify="space-between" spacing="1">
<Stack spacing={{ base: '5', sm: '6' }} shouldWrapChildren>
<InputGroup>
<InputLeftElement pointerEvents="none">
<Icon as={FiSearch} color="muted" boxSize="5" />
</InputLeftElement>
<Input placeholder="Search" />
</InputGroup>
<Stack spacing="1" >
<NavButton label="Home" fontWeight="normal"/>
<NavButton label="Dashboard" aria-current="page" fontWeight="normal" />
<NavButton label="Tasks" fontWeight="normal" />
<NavButton label="ddd" fontWeight="normal"/>
</Stack>
</Stack>
<Stack spacing={{ base: '5', sm: '6' }}>
<Stack spacing="1">
<NavButton label="Library" fontWeight="normal" />
<NavButton label="Help" fontWeight="normal"/>
<NavButton label="Settings" fontWeight="normal"/>
</Stack>
{/* <Box bg="bg-subtle" px="4" py="5" borderRadius="lg">
<Stack spacing="4">
<Stack spacing="1">
<Text fontSize="sm" fontWeight="medium">
Almost there
</Text>
<Text fontSize="sm" color="muted">
Fill in some more information about you and your person.
</Text>
</Stack>
<Progress value={80} size="sm" aria-label="Profile Update Progress" />
<HStack spacing="3">
<Button variant="link" size="sm">
Dismiss
</Button>
<Button variant="link" size="sm" colorScheme="blue">
Update profile
</Button>
</HStack>
</Stack>
</Box> */}
<Divider />
<UserProfile
name="Asdfon"
// image="adsf"
title="adsf, asdf"
/>
</Stack>
</Stack>
</Flex>
</Flex>
)
const Navbar = () => {
const { isOpen, onToggle, onClose } = useDisclosure()
return (
<Box
width="full"
maxW="100%"
py="4"
px={0}
// px={{ base: '4', md: '8' }}
bg="bg-surface"
boxShadow={useColorModeValue('sm', 'sm-dark')}
>
<Flex justify="space-between">
<ToggleButton isOpen={isOpen} aria-label="Open Menu" onClick={onToggle} />
<Drawer
isOpen={isOpen}
placement="left"
onClose={onClose}
isFullHeight
preserveScrollBarGap
// Only disabled for showcase
trapFocus={false}
>
<DrawerOverlay />
<DrawerContent>
<Sidebar />
</DrawerContent>
</Drawer>
</Flex>
</Box>
)
}
interface NavButtonProps extends ButtonProps {
// icon: As
label: string
}
const NavButton = (props: NavButtonProps) => {
const { label, ...buttonProps } = props
return (
<Button variant="ghost" justifyContent="start" {...buttonProps}>
<HStack spacing="3">
{/* <Icon as={icon} boxSize="6" color="subtle" /> */}
<Text>{label}</Text>
</HStack>
</Button>
)
}
const DashBase = () => {
const isDesktop = useBreakpointValue({ base: false, lg: true })
return (
<Flex
as="section"
direction={{ base: 'column', lg: 'row' }}
height="100vh"
bg="white"
mb="100px"
overflowY="auto"
minW="100%"
px={0}
mx="0px"
// minW="120em"
// margin="auto"
>
{isDesktop ? <Sidebar /> : <Navbar />}
<Container py="8" flex="1">
<Stack spacing={{ base: '8', lg: '6' }}>
<Stack
spacing="4"
direction={{ base: 'column', lg: 'row' }}
// justify="space-between"
align={{ base: 'start', lg: 'center' }}
>
<Stack spacing="1">
<Heading size={useBreakpointValue({ base: 'xs', lg: 'sm' })} fontWeight="medium">
Dashboard
</Heading>
<Text color="muted">All important metrics at a glance</Text>
</Stack>
<HStack spacing="3">
<Button variant="secondary" leftIcon={<FiDownloadCloud fontSize="1.25rem" />}>
Download
</Button>
<Button variant="primary">Create</Button>
</HStack>
</Stack>
{/* <Stack spacing={{ base: '5', lg: '6' }} maxW="100%">
<SimpleGrid columns={{ base: 1, md: 3 }} gap="6">
<Card />
<Card />
<Card />
</SimpleGrid>
</Stack> */}
<Card minH="sm" />
</Stack>
</Container>
</Flex>
)
}
export default DashBase;
You could wrap your NavButton in Link
<Link href="/settings">
<NavButton label="Home" fontWeight="normal"/>
</Link>
In pages/settings.tsx, have a component that renders your settings page.
In _app.tsx, pass Component as a child to Sidebar
<Sidebar>
<Component {...pageProps} />
</Sidebar>
In Sidebar, add a param for children. Component will be in the children props. Render children prop after the menu.
const Sidebar = ({ children }) => (
...
</Flex>
{children}
</Flex>
)
I've tried to encapsulate my ListItem component with <Link to"/create> but it doesn't work, i've also tried using the history option but that confused me.
My codesandbox:
codesandbox.io/s/funny-einstein-deuqhi?file=/src/App.js
This is my sidebar.js (without all the imports and the creation of mixin&drawerheader and co, the link on the appbar to my createsite works, i want the same for my sidebar listitems. i want my listitem with the text ."add to-do" to link to the "\create" page):
const openedMixin = (theme) => (...);
const closedMixin = (theme) => (...);
const DrawerHeader = styled(...);
const AppBar = styled(...);
const Drawer = styled(...);
export default function MiniDrawer() {
const theme = useTheme();
const [open, setOpen] = React.useState(false);
const handleDrawerOpen = () => {
setOpen(true);
};
const handleDrawerClose = () => {
setOpen(false);
};
const itemsList = [
{
text: "Calendar",
icon: <EventAvailableOutlinedIcon style={{ fill: 'white' }} />,
},
{
text: "Add To-do",
icon: <AddBoxTwoToneIcon style={{ fill: 'white' }} />
}
]
return (
<Router>
<Box sx={{ display: 'flex' }}>
<CssBaseline />
<AppBar position="fixed" open={open} style={{ background: 'white' }}>
<Toolbar>
<IconButton
color="inherit"
aria-label="open drawer"
onClick={handleDrawerOpen}
edge="start"
sx={{
marginRight: 5,
...(open && { display: 'none' }),
}}
>
<MenuIcon style={{ fill: '#85CEE9' }} />
</IconButton>
<Link to="/create">create</Link>
<Typography variant="h6" noWrap component="div" style={{ color: "#85CEE9" }}>
project
</Typography>
</Toolbar>
</AppBar>
<Drawer variant="permanent" open={open}>
<DrawerHeader>
<IconButton onClick={handleDrawerClose}>
{theme.direction === 'rtl' ? <ChevronRightIcon style={{ fill: 'white' }} /> : <ChevronLeftIcon style={{ fill: 'white' }} />}
</IconButton>
</DrawerHeader>
<Divider />
<List>
{itemsList.map((item, index) => {
const { text, icon } = item;
return (
// <Link to="/create">
<Link to="/create">
<ListItem button component={Link} to="/create" onClick={onItemClick('Page 2')} key={text}>
{icon && <ListItemIcon >{icon}</ListItemIcon>}
// <ListItemText primary={text} style={{ color: "white" }} />
<Link to="/create">create</Link>
</ListItem>
</Link>
// </Link>
);
})}
</List>
</Drawer>
</Box>
</Router>
);
}```
In the sandbox you linked you weren't using the Link component at all in the drawer, so nothing was being linked to.
Render the ListItem component as a Link component and pass the appropriate link props.
Example:
const itemsList = [
{
text: "Calendar",
icon: <EventAvailableOutlinedIcon style={{ fill: "white" }} />,
to: "/create" // <-- add link targets
},
{
text: "Add To-do",
icon: <AddBoxTwoToneIcon style={{ fill: "white" }} />,
to: "/add-todo"
}
];
To the ListItem component, specify the Link component to be the component to render the list item as, and pass the current mapped item's to property.
<List>
{itemsList.map((item, index) => {
const { text, icon } = item;
return (
<ListItem component={Link} to={item.to} button key={text}>
{icon && <ListItemIcon>{icon}</ListItemIcon>}
<ListItemText primary={text} style={{ color: "white" }} />
</ListItem>
);
})}
</List>
Instead of using Link within your List component, use ListItem, ListItemButton, and ListItemText:
<List>
{['Inbox', 'Starred', 'Send email', 'Drafts'].map((text, index) => (
<ListItem key = {text} disablePadding>
<ListItemButton onClick = {handleNav} >
<ListItemIcon>
{index % 2 === 0 ? <InboxIcon /> : <MailIcon />}
</ListItemIcon>
<ListItemText primary={text} />
</ListItemButton>
</ListItem>
))}
</List>
Create the method to pass to each ListItemButton's onClick:
import { useHistory } from 'react-router-dom'
const history = useHistory()
const handleNav = (event) =>
{
// Do whatever you need to do then push to the new page
history.push('/create')
}
Depending on what version of react-router you're using, your actual method to push to a new page may vary (useHistory for v5, useNavigate for v6, etc.).
I am upgrading from v4 to v5 and having significant problems that are inconsistent.
The <Button> component looks as it should on the Login page, but once logged in, the <Button> components look like they don't have any MUI styling, such as padding and color. I am thinking it has something to do with the themes because some files complain about theme.spacing while others do not.
I have no idea what I am doing wrong.
App.tsx
const theme = createTheme(
adaptV4Theme({
palette: {
primary: {
light: '#e57373',
main: '#d32f2f',
dark: '#d32f2f',
contrastText: '#fff',
},
secondary: {
light: '#9adcfb',
main: '#03a9f4',
dark: '#0288d1',
contrastText: '#fff',
},
},
}),
);
const LoggedInRouter = lazy(() => import('./routers/LoggedInRouter'));
const App: React.FC = () => {
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={theme}>
<IonApp>
<IonReactRouter>
<Suspense fallback={<Loading />}>
<IonRouterOutlet>
<Switch>
<Route path="/login" component={Login} />
<Route path="/user" component={LoggedInRouter} />
<Route path="/" render={() => <Redirect to="/login" />} />
</Switch>
</IonRouterOutlet>
</Suspense>
</IonReactRouter>
</IonApp>
</ThemeProvider>
</StyledEngineProvider>
);
};
Login.tsx (Button looks as it should if compilation error theme.spacing is commented out)
/login
const useStyles = makeStyles(theme => ({
root: {
height: '100vh',
},
paper: {
height: '75vh',
margin: theme.spacing(8, 4),
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
},
logo: {
maxWidth: '100%',
},
}));
const Login = (): ReactElement => {
const classes = useStyles();
return (
<>
<Grid container className={classes.root}>
<Grid item sm={6} component={Paper} elevation={6} square>
<div className={classes.paper}>
<Paper elevation={3}>
<div className={classes.paper}>
<Typography variant="h5">Log In To Your Account</Typography>
<p></p>
<div className={classes.root}>
<Button
type="button"
fullWidth
variant="contained"
color="primary"
disabled
>
Create Account
</Button>
</div>
</div>
</Paper>
</div>
</Grid>
</Grid>
</>
);
};
Settings.tsx (Buttons and other basic MUI components don't have any expected styling as shown in docs)
/user/settings
const useStyles = makeStyles(theme => ({
margin: {
margin: theme.spacing(3),
},
padding: {
padding: theme.spacing(3),
},
connectionCard: {
width: '100%',
},
}),
);
const Settings = (): ReactElement => {
const classes = useStyles();
const [value, setValue] = React.useState(0);
return (
<>
<Paper className={classes.margin} elevation={3}>
<Grid container spacing={2} className={classes.padding}>
<Grid item md={4}>
<Card className={classes.connectionCard} raised={true}>
<CardContent>
<Typography variant="body1" component="p">
Test
</Typography>
</CardContent>
<CardActions>
<Button variant="contained" color="secondary">
Connect
</Button>
</CardActions>
</Card>
</Grid>
</Grid>
</Paper>
</>
);
};
https://mui.com/guides/migration-v4/#styles-broken-after-migrating-to-v5
When initially looking, I did not see this very last thing in the troubleshooting section. Maybe it was added recently, or I just missed it.
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.
I cannot get the updated state of isAuthenticated when I have Material-UI code.
Is there any way that I can update the state when I have Material-UI code.
import React from "react";
import clsx from "clsx";
import { withStyles } from "#material-ui/core/styles";
import MUILink from "#material-ui/core/Link";
import AppBar from "../UI/components/AppBar";
import Toolbar, { styles as toolbarStyles } from "../UI/components/Toolbar";
import { Link } from "react-router-dom";
import { connect } from 'react-redux';
const styles = theme => ({
title: {
fontSize: 24
},
placeholder: toolbarStyles(theme).root,
toolbar: {
justifyContent: "space-between"
},
left: {
flex: 1
},
leftLinkActive: {
color: theme.palette.common.white
},
right: {
flex: 1,
display: "flex",
justifyContent: "flex-end"
},
rightLink: {
fontSize: 16,
color: theme.palette.common.white,
marginLeft: theme.spacing(3)
},
linkSecondary: {
color: theme.palette.secondary.main
}
});
const Navbar = (props,{isAuthenticated,loading}) => {
const { classes } = props;
const authLinks = (
<div className={classes.right}>
<MUILink
variant="h6"
underline="none"
component={Link} to="/log-out"
className={clsx(classes.rightLink, classes.linkSecondary)}
>
{"Log out"}
</MUILink>
</div>
);
const guestLinks = (
<div className={classes.right}>
<MUILink
color="inherit"
variant="h6"
underline="none"
component={Link} to="/sign-in"
className={classes.rightLink}
>
{"Sign In"}
</MUILink>
<MUILink
variant="h6"
underline="none"
component={Link} to="/sign-up"
className={clsx(classes.rightLink, classes.linkSecondary)}
>
{"Sign Up"}
</MUILink>
</div>
);
return (
<div>
<AppBar position="fixed">
<Toolbar className={classes.toolbar}>
<div className={classes.left} />
<MUILink
variant="h6"
underline="none"
color="inherit"
className={classes.title}
component={Link} to="/"
>
{"buzzer"}
</MUILink>
{!loading && (
<React.Fragment>{isAuthenticated ? authLinks : guestLinks}</React.Fragment>
)}
</Toolbar>
</AppBar>
<div className={classes.placeholder} />
</div>
);
};
const mapStateToProps = state =>({
isAuthenticated:state.auth.isAuthenticated,
loading:state.auth.loading
})
export default connect(mapStateToProps)((withStyles(styles)(Navbar)));
I want that the Navbar to change based on the condition of isAuthenticated. If user is authenticated I want to display only authLink, if the user is not authenticated I want to display the guestLink.
{isAuthenticated,loading} will be injected inside props, and not as a second parameter:
const Navbar = (props) => {
const { classes, isAuthenticated,loading } = props;