JS24
JS24

Reputation: 458

Each child in a list should have a unique "key" prop. React js

so i try to render an array of object into react js component like below:

import React, { useState } from "react";

import { Route, Link } from "react-router-dom";

import {
  MdOutlineSpaceDashboard,
  MdOutlineStorage,
  MdOutlineFactCheck,
  MdOutlineCalculate,
  MdStickyNote2,
  MdAssignmentTurnedIn,
  MdOutlineDynamicForm,
  MdOutlineArrowDropDown,
} from "react-icons/md";
import { BsChevronDown, BsArrowLeftShort } from "react-icons/bs";

import Logo_Nabati from "../assets/logo-nabati.svg";

const menuItems = [
  { id: 1, label: "Dashboard", icon: MdOutlineSpaceDashboard, link: "/" },
  {
    id: 2,
    label: "Master Data",
    icon: MdOutlineStorage,
    iconArrow: MdOutlineArrowDropDown,
    link: "",
    subMenu: true,
    subMenuItems: [
      { id: 1, label: "KSBT", link: "/MasterData/list/KSBT" },
      { id: 2, label: "SQ01_RM", link: "/MasterData" },
      { id: 3, label: "SQ01_PM", link: "/MasterData" },
      { id: 4, label: "Depre", link: "/MasterData" },
      { id: 5, label: "OMC", link: "/MasterData" },
      { id: 6, label: "Premix", link: "/MasterData" },
      { id: 7, label: "Routing", link: "/MasterData" },
      { id: 8, label: "MP", link: "/MasterData" },
    ],
  },
  { id: 3, label: "Check COGM", icon: MdOutlineFactCheck, link: "/checkcogm" },
  {
    id: 4,
    label: "Calculation",
    icon: MdOutlineCalculate,
    link: "/calculation",
  },
  {
    id: 5,
    label: "Draft Calculation",
    icon: MdStickyNote2,
    link: "/draft",
  },
  { id: 6, label: "Approved", icon: MdAssignmentTurnedIn, link: "/approval" },
  { id: 7, label: "Task Activity", icon: MdOutlineDynamicForm, link: "/task" },
];

const Sidebar = () => {
  const [open, setOpen] = useState(false);
  const [submenuOpen, setSubmenuOpen] = useState(false);
  return (
    <div className="flex">
      <div
        className={` bg-yellow-400 h-screen p-5 pt-8  ${
          open
            ? "w-50 ease-out delay-150 peer-focus:left-0 duration-200"
            : "w-20 ease-out delay-150 peer-focus:left-0 duration-200"
        } duration-300 relative`}
      >
        <BsArrowLeftShort
          className={` bg-white text-yellow-300 text-3xl rounded-full absolute -right-3 top-9 border border-yellow-300 cursor-pointer delay-150 duration-200  ${
            !open && "rotate-180"
          }`}
          onClick={() => setOpen(!open)}
        />
        <div className={`inline-flex`}>
          <img src={Logo_Nabati} width={123} height={75} alt="logo Nabati" />
        </div>
        <ul className="pt-8">
          {menuItems.map(
            ({ icon: Icon, iconArrow: IconArrow, ...menu }, index) => (
              <>
                <Link to={menu.link}>
                  <li
                    key={index}
                    className="text-white text-sm text-justify flex items-center gap-x-4 cursor-pointer p-2 hover:bg-red-600 rounded-md mt-2"
                  >
                    <Icon className="text-2xl text-white group-hover:text-red-600" />

                    <span
                      className={`text-base font-mendium flex-1 duration-200 ${
                        !open && "hidden"
                      } `}
                    >
                      {menu.label}
                    </span>
                    {menu.subMenu && (
                      <BsChevronDown
                        className={`text-base font-mendium  duration-200 ${
                          !open && "hidden"
                        } ${submenuOpen && "rotate-180"}`}
                        onClick={() => {
                          setSubmenuOpen(!submenuOpen);
                        }}
                      />
                    )}
                  </li>{" "}
                </Link>
                {menu.subMenu && submenuOpen && open && (
                  <ul>
                    {menu.subMenuItems.map((subMenuItem, index) => (
                      <Link to={subMenuItem.link}>
                        <li
                          key={index}
                          className="text-white text-sm flex items-center gap-x-4 cursor-pointer p-1 px-12 hover:bg-red-500 rounded-md"
                        >
                          {subMenuItem.label}
                        </li>{" "}
                      </Link>
                    ))}
                  </ul>
                )}
              </>
            )
          )}
        </ul>
      </div>
    </div>
  );
};

export default Sidebar;

even after i put the key={index} on the

  • component i still got warning like this

    react-jsx-dev-runtime.development.js:119 Warning: Each child in a list should have a unique "key" prop.
    

    can someone tell me where di i do wrong here, it supposed to be no problem after i put the key={item} but why i still gettingn error on the console?

    Upvotes: 0

    Views: 86

  • Answers (2)

    John Ruddell
    John Ruddell

    Reputation: 25842

    A more proper way to handle this issue is to use a React.Fragment element. this way you do not pollute your DOM tree with unnecessary wrapper elements. Also in the accepted answer, the DOM tree is adding a div in between a ul and li set. Which is invalid HTML semantics and you'll undoubtedly see errors in the console for this.

    ...
    
    <ul className="pt-8">
      {menuItems.map(({ icon: Icon, iconArrow: IconArrow, ...menu }) => (
        <React.Fragment key={menu.id}>
          ...
        </React.Fragment>
      ))}
    </ul>
    ...
    

    One way React optimizes + determines when a component / element needs a new render is with a key. You can specify it on any element to give extra control. However, when you are dynamically rendering elements via some form of a loop (i.e. mapping over an array and returning jsx), React needs some extra help knowing how to track the items key. This item needs to be the top most element. So this is why it needs to be on the Fragment.

    Upvotes: 1

    Miguel Caro
    Miguel Caro

    Reputation: 298

    do the following

    import React, { useState } from "react";
    
    import { Route, Link } from "react-router-dom";
    
    import {
      MdOutlineSpaceDashboard,
      MdOutlineStorage,
      MdOutlineFactCheck,
      MdOutlineCalculate,
      MdStickyNote2,
      MdAssignmentTurnedIn,
      MdOutlineDynamicForm,
      MdOutlineArrowDropDown,
    } from "react-icons/md";
    import { BsChevronDown, BsArrowLeftShort } from "react-icons/bs";
    
    import Logo_Nabati from "../assets/logo-nabati.svg";
    
    const menuItems = [
      { id: 1, label: "Dashboard", icon: MdOutlineSpaceDashboard, link: "/" },
      {
        id: 2,
        label: "Master Data",
        icon: MdOutlineStorage,
        iconArrow: MdOutlineArrowDropDown,
        link: "",
        subMenu: true,
        subMenuItems: [
          { id: 1, label: "KSBT", link: "/MasterData/list/KSBT" },
          { id: 2, label: "SQ01_RM", link: "/MasterData" },
          { id: 3, label: "SQ01_PM", link: "/MasterData" },
          { id: 4, label: "Depre", link: "/MasterData" },
          { id: 5, label: "OMC", link: "/MasterData" },
          { id: 6, label: "Premix", link: "/MasterData" },
          { id: 7, label: "Routing", link: "/MasterData" },
          { id: 8, label: "MP", link: "/MasterData" },
        ],
      },
      { id: 3, label: "Check COGM", icon: MdOutlineFactCheck, link: "/checkcogm" },
      {
        id: 4,
        label: "Calculation",
        icon: MdOutlineCalculate,
        link: "/calculation",
      },
      {
        id: 5,
        label: "Draft Calculation",
        icon: MdStickyNote2,
        link: "/draft",
      },
      { id: 6, label: "Approved", icon: MdAssignmentTurnedIn, link: "/approval" },
      { id: 7, label: "Task Activity", icon: MdOutlineDynamicForm, link: "/task" },
    ];
    
    const Sidebar = () => {
      const [open, setOpen] = useState(false);
      const [submenuOpen, setSubmenuOpen] = useState(false);
      return (
        <div className="flex">
          <div
            className={` bg-yellow-400 h-screen p-5 pt-8  ${
              open
                ? "w-50 ease-out delay-150 peer-focus:left-0 duration-200"
                : "w-20 ease-out delay-150 peer-focus:left-0 duration-200"
            } duration-300 relative`}
          >
            <BsArrowLeftShort
              className={` bg-white text-yellow-300 text-3xl rounded-full absolute -right-3 top-9 border border-yellow-300 cursor-pointer delay-150 duration-200  ${
                !open && "rotate-180"
              }`}
              onClick={() => setOpen(!open)}
            />
            <div className={`inline-flex`}>
              <img src={Logo_Nabati} width={123} height={75} alt="logo Nabati" />
            </div>
            <ul className="pt-8">
              {menuItems.map(
                ({ icon: Icon, iconArrow: IconArrow, ...menu }, index) => (
                  <div key={menu.id}>
                    <Link to={menu.link}>
                      <li
                        className="text-white text-sm text-justify flex items-center gap-x-4 cursor-pointer p-2 hover:bg-red-600 rounded-md mt-2"
                      >
                        <Icon className="text-2xl text-white group-hover:text-red-600" />
    
                        <span
                          className={`text-base font-mendium flex-1 duration-200 ${
                            !open && "hidden"
                          } `}
                        >
                          {menu.label}
                        </span>
                        {menu.subMenu && (
                          <BsChevronDown
                            className={`text-base font-mendium  duration-200 ${
                              !open && "hidden"
                            } ${submenuOpen && "rotate-180"}`}
                            onClick={() => {
                              setSubmenuOpen(!submenuOpen);
                            }}
                          />
                        )}
                      </li>{" "}
                    </Link>
                    {menu.subMenu && submenuOpen && open && (
                      <ul>
                        {menu.subMenuItems.map((subMenuItem, index) => (
                          <Link to={subMenuItem.link} key={`${menu.id}-${subMenuItem.id}`}>
                            <li
                              key={index}
                              className="text-white text-sm flex items-center gap-x-4 cursor-pointer p-1 px-12 hover:bg-red-500 rounded-md"
                            >
                              {subMenuItem.label}
                            </li>{" "}
                          </Link>
                        ))}
                      </ul>
                    )}
                  </div>
                )
              )}
            </ul>
          </div>
        </div>
      );
    };
    
    export default Sidebar;
    

    you need to add the key to the first element inside the map in your code the first element is a fragment

    <></>
    

    that way is not working when you do it on the

    <li key={index}
    

    also avoid to use index it will trigger a warning and is better practice use a unique identifier

    Upvotes: 1

    Related Questions