Objective
Understand the root cause of the "Each child in a list should have a unique key prop" warning raised when using a React Fragment and eliminate it.
Background
For a while now, I've ignored a React warning in this site's archive page code when rendering a list of historic posts. Given the issue raised is a warning, I deprioritized it. But, the reoccurrence of the warning finally got to me and I decided to explore why it was happening and how to fix it.
Here's the warning:
Warning: Each child in a list should have a unique "key" prop. See https://reactjs.org/link/warning-keys for more information. at PostsLists (webpack-internal: ///./components/posts-lists.js:10:22)
The first thing I did was to clean up the code to isolate the offending fragment. The code used several nested fragments.
Here's the cleaned up code:
import PostListItem from './post-listitem'
export default function PostsList({listTitle, posts, years, month}) {
if (!posts || !Array.isArray(posts)) return null;
return (
<>
{
years.map((year, y) => {
return (
<>
<h3 key={y} className="text-lg font-bold mb-1" >{year}{(month)?" / " + month:""}</h3>
{
posts.map((post, i) => {
if (post.year == year)
return (
<PostListItem key={i} post={post} />
)
return null
})
}
</>
)
})
}
</>
)
}
Root Cause and Solution
It turns out my mistake was using a shorthand React Fragment (<>) and adding a key prop to the wrong HTML element while mapping years. I originally added the shorthand Fragment because that map returns multiple components and I did not want to add an additional div to the resulting HTML.
Here's the revised code. Note that I had to import React here to use the full Fragment syntax and that the React.Fragment now carries the key prop instead of the H3 element.
import PostListItem from './post-listitem'
import React from 'react';
export default function PostsList({listTitle, posts, years, month}) {
if (!posts || !Array.isArray(posts)) return null;
return (
<>
{
years.map((year, y) => {
return (
<React.Fragment key={y}>
<h3 className="text-lg font-bold mb-1" >{year}{(month)?" / " + month:""}</h3>
{
posts.map((post, i) => {
if (post.year == year)
return (
<PostListItem key={i} post={post} />
)
return null
})
}
</React.Fragment>
)
})
}
</>
)
}