Home > Blockchain >  MaterialUI React duplicate keys error from mui components
MaterialUI React duplicate keys error from mui components

Time:02-19

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!

CodePudding user response:

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>

CodePudding user response:

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