Material UI React 来自 mui 组件的重复键错误

MaterialUI React duplicate keys error from mui components

在创建导航栏时,我使用了 MUI 中的多个折叠组件来创建生成菜单项。

这不是一个代码破坏错误,但有一个很烦人。我的控制台出现以下错误。

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

我没有在我的代码中设置这个键,但为什么默认设置它,哪个组件一次又一次地被分配相同的键,我该如何解决这个问题。

我的导航条代码如下,供大家参考。

    //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

几乎无处不在的随机密钥属性是我寻找哪个组件产生此错误但没有运气的追求。

一如既往地提前致谢,如果您有任何建议或解决方案...我洗耳恭听!

你可以试试这个。

<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>

这里:

 {menuItems.items.map(({id, icon, title, children}) => (
        <>
          <List
            key={id+'ParentList'}

您的密钥必须在第一个 child 上提供,在您的情况下是 React Fragment,因此: {menuItems.items.map(({id, icon, title, children}) => (

        <Fragment key={id+'ParentList'}>
          <List ...

你需要放一个uniq key,如果你使用2次(0, 1, 2, 3 === 0, 1, 2, 3),map中的索引可以提供同样的问题,你可以使用添加字符串或者您可以使用方法生成 uniq 键:

  • uuid nom 包
  • 来自 redux-toolkit 的 nanoid(如果你使用 Redux 就完美了,不要为此安装它)
  • 从 Math.random 创建一个方法,例如 Math.floor(Math.random()*10000000).toString(16) (=> 六键)
  • 根据您在方法参数中提供的前缀创建方法,并从新的 Date() 或时间戳添加元素
  • ...

有了这个,您一定要创建一个新的唯一密钥。

随着时间的推移,按顺序,我更喜欢Nanoid或Math random。