I'm getting my navbar routes from a json file like this:
{
"categorias": [
{
"nombre": "Faltas de profesorado",
"componentes": [
{
"type": "url",
"icon": "fas fa-plus",
"title": "Nueva falta",
"url": "/"
},
{
"type": "url",
"icon": "fas fa-comment",
"title": "Gestionar faltas",
"url": "/"
},{
"type": "desplegable",
"title": "Reservar un moscoso",
"url": "/",
"items": [
{
"type": "url",
"title": "Moscoso de urgencia",
"url": "/"
}
]
}
]
}
]
}
Then I map them in my component.
I tried changing all keys to nanoid(6) to get random strings. Still no success.
const NavRoutes = ({rutas}) => (
/* Mapeo de rutas */
rutas.map(ruta => (<div className={styles.NavigationBar__routes__wrapper} key={nanoid(6)}>
<div open={true} className={styles.NavigationBar__routes__name__container} onClick={(e) => e.currentTarget.toggleAttribute('open')}>
<i className={`${styles.icon} fas fa-arrow-right`}/>
<h5 className={styles.NavigationBar__routes__name}>{ruta.nombre}</h5>
</div>
<div className={styles.NavigationBar__routes__container}>
{
(ruta.componentes?.length > 0)?
ruta.componentes.map((subRuta,i) => (
<ul key={nanoid(6)} className={styles.NavigationBar__route__list}>
<li key={nanoid(6)}>
<i className={`${styles.icon} ${subRuta.icon}`}/>
<Link to={`${subRuta.url}`}>{subRuta.title}</Link>
</li>
{
(subRuta.items?.length > 0 && subRuta.type === 'desplegable')
?
<ul key={nanoid(6)}>
{subRuta.items.map((item,i) => (<>
<li key={nanoid(6)}>
<Link to={`${item.url}`}>{item.title}</Link>
</li>
{(item.items?.length > 0)
?
item.items.map(extra => (
<ul key={nanoid(6)}>
<li><Link to={`${item.url}`}>{extra.title}</Link></li>
</ul>
))
:
''
}
</>))}
</ul>
:
''
}
</ul>
)
)
:
'Vacio'
}
</div>
</div>))
/* Fin mapeo de rutas */
)
And mapping them in my NavBar component.
Everything works but in my console appears the error, even though I have alerady defined a key after each mapped element (no elements have the same key):
should have a unique "key" prop. Check the render method of `NavRoutes`. See https://reactjs.org/link/warning-keys for more information. at NavRoutes (http://localhost:3000/static/js/bundle.js:544:5) at div at div at http://localhost:3000/static/js/bundle.js:33611:5 at nav at NavBar (http://localhost:3000/static/js/bundle.js:675:69) at div at Home at Index (http://localhost:3000/static/js/bundle.js:1826:66) at Routes (http://localhost:3000/static/js/bundle.js:64433:5) at Router (http://localhost:3000/static/js/bundle.js:64366:15) at BrowserRouter (http://localhost:3000/static/js/bundle.js:63842:5) at AuthProvider (http://localhost:3000/static/js/bundle.js:1434:5) at App ```
Can someone take a look? I've tried many things none work.
FIXED
The error was here, the fragment had no key.
subRuta.items.map((item,i) => (<>
<li key={nanoid(6)}>
<Link to={`${item.url}`}>{item.title}</Link>
</li>
{(item.items?.length > 0)
?
item.items.map(extra => (
<ul key={nanoid(6)}>
<li><Link to={`${item.url}`}>{extra.title}</Link></li>
</ul>
))
:
''
}
</>))
Changed the fragment to a ul. Thank you all.
subRuta.items.map((item,i) => (<ul key={nanoid(6)}>
<li>
<Link to={`${item.url}`}>{item.title}</Link>
</li>
{(item.items?.length > 0)
?
item.items.map(extra => (
<ul key={nanoid(6)}>
<li><Link to={`${item.url}`}>{extra.title}</Link></li>
</ul>
))
:
''
}
</ul>))
CodePudding user response:
In addition to @gwalsgington answer, you can use a random string generator like nanoid to your project. Then you can use it like this after importing nanoid as import { nanoid } from 'nanoid'
:
{subRuta.items.map(item => (<>
<li key={nanoid(4)}>
<Link to={`${item.url}`}>{item.title}</Link>
</li>
{(item.items?.length > 0)
?
item.items.map(extra => (
<ul key={nanoid(4)}>
<li><Link to={`${item.url}`}>{extra.title}</Link></li>
</ul>
))
:
''
}
CodePudding user response:
You should set the key to the index, not the title. This way if you have duplicate titles, it won't have a duplicate key.
{subRuta.items.map((item, index) => (<>
<li key={index}>
<Link to={`${item.url}`}>{item.title}</Link>
</li>
{(item.items?.length > 0)
?
item.items.map((extra, index) => (
<ul key={index}>
<li><Link to={`${item.url}`}>{extra.title}</Link></li>
</ul>
))
:
''
}
Additionally, you're giving keys to wayyyy too many divs. Look at the first map - there's a key on every div. Remove all that. You only need a key on the first div of a loop.
There are 3 keys in this one section, and 2 are exactly the same key={'${ruta.nombre}-container'}
. The only key you need is the first one.
<div className={styles.NavigationBar__routes__wrapper} key={ruta.nombre}>
<div key={`${ruta.nombre}-container`} open={true} className={styles.NavigationBar__routes__name__container} onClick={(e) => e.currentTarget.toggleAttribute('open')}>
<i className={`${styles.icon} fas fa-arrow-right`}/>
<h5 className={styles.NavigationBar__routes__name}>{ruta.nombre}</h5>
</div>
<div key={`${ruta.nombre}-list-container`} className={styles.NavigationBar__routes__container}>