Home > Mobile >  Each child should have a unique key prop even they're set - REACT
Each child should have a unique key prop even they're set - REACT

Time:03-26

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}>
  • Related