Unique keys in React

Anyone know why I'm getting that warning here? I've a key for each renderedItem returned, and for the nested ItemComponent so am a bit confused. Haven't spent enough time banging my head against a React wall to spot it, and out of the 17 pages of errors React has provided me with none are helpful. Would appreciate it if someone can spot the error I can't!
export function FilterAccordion({ items, className }) {
const [expandedIndex, setExpandedIndex] = useState(0);

const handleClick = (index) => {
setExpandedIndex((currentExpandedIndex) => {
if (currentExpandedIndex === index) {
return -1;
}
return index;
});
};
const renderedItems = items.map((item, index) => {
const isExpanded = index === expandedIndex;
const ItemComponent = item.content.itemComponent;
const Icon = item.icon;

const classes = twMerge(
`group flex items-center px-3 py-2.5 rounded cursor-pointer bg-slate-100 hover:bg-teal-500 ${
isExpanded ? 'bg-teal-500 text-white' : ''
}`
);

const openIcon = (
<span className=''>
{isExpanded ? <GoChevronDown /> : <GoChevronLeft />}
</span>
);
return (
<div className='my-0.5' key={item.id}>
<div className={classes} onClick={() => handleClick(index)}>
<Icon className='mr-4 w-6 h-6 group-hover:text-white' />
<div className='text-base group-hover:text-white'>{item.name}</div>
<div className='ml-auto group-hover:text-white'>{openIcon}</div>
</div>
{isExpanded && (
<AccordionContent>
{item.content.options.map((option) => {
return <ItemComponent key={option} option={option} />;
})}
</AccordionContent>
)}
</div>
);
});

return <div className={className}>{renderedItems}</div>;
}
export function FilterAccordion({ items, className }) {
const [expandedIndex, setExpandedIndex] = useState(0);

const handleClick = (index) => {
setExpandedIndex((currentExpandedIndex) => {
if (currentExpandedIndex === index) {
return -1;
}
return index;
});
};
const renderedItems = items.map((item, index) => {
const isExpanded = index === expandedIndex;
const ItemComponent = item.content.itemComponent;
const Icon = item.icon;

const classes = twMerge(
`group flex items-center px-3 py-2.5 rounded cursor-pointer bg-slate-100 hover:bg-teal-500 ${
isExpanded ? 'bg-teal-500 text-white' : ''
}`
);

const openIcon = (
<span className=''>
{isExpanded ? <GoChevronDown /> : <GoChevronLeft />}
</span>
);
return (
<div className='my-0.5' key={item.id}>
<div className={classes} onClick={() => handleClick(index)}>
<Icon className='mr-4 w-6 h-6 group-hover:text-white' />
<div className='text-base group-hover:text-white'>{item.name}</div>
<div className='ml-auto group-hover:text-white'>{openIcon}</div>
</div>
{isExpanded && (
<AccordionContent>
{item.content.options.map((option) => {
return <ItemComponent key={option} option={option} />;
})}
</AccordionContent>
)}
</div>
);
});

return <div className={className}>{renderedItems}</div>;
}
9 Replies
Chris Bolson
Chris Bolson13mo ago
I am new to react so please forgive me if I am missing the issue. Presumably item.id is numeric. What values does options have? If there is a chance that the option value might be numeric that could cause the error. It might be the completely wrong approach but for keys I tend to prepend a string to the keys to reflect what type of data it is and specifically to avoid the possibility of having duplicate keys.
eg item-${item.I’d} and option-${option}
Zoë
Zoë13mo ago
key={option} it looks like option is not a valid key type. If you're passing an object when it does key comparisons it sees all keys as [object Object] making it non-unique The error you get should mention this. You want to use something that when converted to a string is unique
JWode
JWode13mo ago
@Chris yeah item.id is just a numeric id. The options object is an array of string options @zed_dash item.content.options is just an array of strings representing the options - not a great key, but should be unique? Or does that not count? Think I might just add an id to see if that fixes it
Zoë
Zoë13mo ago
Ah if option is a string then what I've mentioned isn't the issue, as long as the array contains unique items (It looked like option is an object because you're only passing option={option} to the component which suggests that more than a string is contained)
Chris Bolson
Chris Bolson13mo ago
If options is an object (or an array)then the issue is what @zed_dash has said
Zoë
Zoë13mo ago
Regardless using TypeScript would help make things clear
JWode
JWode13mo ago
Ah, thanks guys, it ended up me being an idiot. That .id class didn't exist on the item. Know it was my fault, but still no idea why React doesnt attach the key to the dom so that obvious errors can be more obvious :/
Zoë
Zoë13mo ago
Classic error React doesn't because it's unnecessary if you develop using TypeScript, that's the kind of error that isn't possible with it
JWode
JWode13mo ago
Yeah, have done a big course on TS recently, am a fan (esp coming from Java). Just have to apply it all to React then Express (which I've heard is a ballache), and then I'll be job ready ) 🙂