React: The Hidden Dangers of Using Index as a Key

React: The Hidden Dangers of Using Index as a Key

In this blog, we will cover:

Why is a Key Needed in React?
Why Avoid Using the Index as a Key?
How Using Index as Key Creates Problems?

Consider this example where we have a list of product categories like Beauty, Fragrances, Furniture, etc. When a category is selected, we display the product details of that category using data from the dummyjson.

Code:

// src/app/page.tsx

import CategoryContainer from “@/components/home-container”;
import “../styles/global.css”;

export default async function Home() {
const response = await fetch(“https://dummyjson.com/products/category-list”);
const data: string[] = await response.json();
return (
<>
<CategoryContainer data={data} />
</>
);
}

// CategoryContainer.tsx

“use client”;

import Image from “next/image”;
import React, { FC, useEffect, useState } from “react”;
import ProductResponse from “./from-type-folder-lets-assume”;

type Props = {
data: string[];
};

const CategoryContainer: FC<Props> = ({ data }) => {
const [selectedCategory, setSelectedCategory] = useState(“”);
const [productDetails, setProductDetails] = useState<ProductResponse | null>(
null
);

useEffect(() => {
(async () => {
const response = await fetch(
“https://dummyjson.com/products/category/” + selectedCategory
);
const data: ProductResponse = await response.json();
setProductDetails(data);
})();
}, [selectedCategory]);

return (
<div className=”max-w-6xl mx-auto my-10″>
{data?.map((item, index) => {
return (
<button
key={index}
type=”button”
onClick={() => setSelectedCategory(item)}
className={`text-gray-900 border border-gray-300 font-medium rounded-full text-sm px-5 py-2.5 me-2 mb-2 uppercase ${
item === selectedCategory ? “bg-gray-600 text-white” : “bg-white”
}`}
>
{item}
</button>
);
})}
<h1 className=”my-5 italic”>
{selectedCategory ? (
<>
Product Details of{” “}
<span className=”font-bold uppercase”>{selectedCategory}</span>
</>
) : (
“Please select a category from above”
)}
</h1>

<div className=”grid grid-cols-3 gap-3″>
{productDetails?.products?.map((product, index) => {
return (
<div
key={index}
className=”max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow dark:bg-gray-800 dark:border-gray-700″
>
<div>
<h2>{product.title}</h2>
<p>{product.description}</p>
<Image
key={index}
width={100}
height={100}
className=”w-full h-full”
src={product.images?.[0]}
alt={product.title}
/>
</div>
</div>
);
})}
</div>
</div>
);
};

export default CategoryContainer;

In the CategoryContainer, we loop through two lists: one for categories and another for product details of the selected category. Initially, we used the index as the key for both loops. This caused issues where images were not updating correctly when switching categories, as shown in this visual

Debugging the Problem
When switching categories, the content updated immediately, but the images took some time to load. This suggested that React wasn’t re-rendering the components correctly.

By examining the code, we noticed that using the index as the key prevented React from recognizing that new components needed to be rendered. React was reusing components with old images, leading to the delay

Solution
We needed to provide a unique key for each product. Fortunately, the API response included a unique id for each product:

Using product.id as the key resolved the issue, ensuring React correctly identified and rendered new components:

Conclusion
In React, always use a unique identifier for keys instead of the index. This ensures efficient and correct re-rendering of components, preventing issues like delayed image updates or incorrect component states.