Search code examples
reactjskeyunique

Where should I add the unique key attribute when mapping through an object


I can't seem to figure out where to put the unique key when mapping through my objects. When running, keep getting a "Warning: Each child in a list should have a unique "key" prop." warning. I've just wound up adding keys to all the children elements. Can someone pls help out on this one? Tech: NextJS/TailwindCss

const projectInfo = [
    {
        id: "react-movie-search",
        name: "React-movie-search",
        goal: "This app pulls from The Movie Database(TMDB) Api to find general info on quite a few movies.",
        description: "This SPA pulls data from the TMDB Api using a simple fetch call.",
        techStack: "React, SASS, TMDB Api, and Vercel",
        screenShot: "images/movie-search-screenshot.png",
        url: "https://tmdb-movie-search-cxueep507-gsdablessedfist.vercel.app/",
        git: "https://github.com/GSdaBlessedFist/TMDB-Movie-Search"
    }
];
import projectPieces from "../data/projectInfo";
const [projects,setProjects] = useState(projectPieces);
const WebDevSection = ({projects})=>{
  return(<>
    <section className="container mx-auto  border border-lime-400 border-4 flex flex-col " id="web-development">
      <table className=" table table-auto b">
        <tbody>
          <tr id="webDev-title-row">
            <td className="pl-6 pt-4 text-2xl text-white/75">
              web development<br/>
              <span className="pl-8 relative top-[-1rem] text-6xl ">Projects</span>
            </td>
          </tr>
          <tr id="webDev-links-row" className="flex flex-wrap justify-evenly">
            {projects.map((project,index)=>{
              return (<>
                <td className="shrink-0 p-2 button" key={index}>
                  <Link href={`#${project.id}`} key={`${project.name}-link`}>
                    <a className=" h-full" id={`${project.id}-link`} key={`${project.name}-anchor`}>
                      {project.name}
                    </a>
                  </Link>
                </td>
              </>)
            })}
          </tr>
          <tr id="webDev-items-row" className="flex flex-col pt-4 b">
            {projects.map((project,index)=>{
              return (<>
                <td id={`${project.id}`} className="w-11/12 mx-auto my-1 b" key={project.id}>
                  <div id={`${project.id}-grid`} className="grid grid-cols-[30%_auto] grid-rows-[70%_18%_auto] h-[250px] b">
                    <div id={`${project.id}-screenshot`} className="col-span-1 row-span-2 m-auto b">insert image</div>
                    <div id={`${project.id}-info`} className="p-4 pt-1  b">
                      <h1 id={`${project.id}-title`} className="text-center py-1 font-bold ">{project.name}</h1>
                      <p id={`${project.id}-description`} className="text-sm leading-4">{project.description}</p>
                    </div>
                    <div id={`${project.id}-techStack`} className="text-xs flex items-center justify-center p-2 text-center b">
                      {project.techStack}
                    </div>
                    <div id={`${project.id}-external-links-group`} className="col-span-2 row-start-3 flex items-center justify-evenly  b">
                      <Link href={project.url} className="">
                        <a id={`${project.id}-url`} className="font-bold button px-4" target="_blank">live site</a>
                      </Link>
                      <div className="spacer"> </div>                                         
                      <Link href={project.git}>
                        <a id={`${project.id}-github`} className="font-bold" target="_blank">github</a>
                      </Link>
                    </div>
                  </div>
                </td>
              </>)
            })}
          </tr>
        </tbody>
      </table>

    </section>
  </>)
}

Solution

  • TLDR: The key should be added on the root tag returned by the map function

    Since you're using a fragment wrapping your td tag, you will add the key attribute to your fragment like <React.Fragment key={...}>.

    <tr id="webDev-items-row" className="flex flex-col pt-4 b">
      {projects.map((project,index) => {
        return (
          <React.Fragment key={project.id}>
            <td>...</td>
          <React.Fragment/>
        )
      })}
    </tr>
    

    BTW, you can remove the fragment since you're returning a single child. If you remove it, you will add the key to your <td key={project.id} id={...}>...</td>

    Another tip is to avoid using the array index on your key since the key.