Diagnostics
LUM2008: Unsupported list key
The key of a .map() row cannot be compiled, so the list ignores it and matches rows by reference.
Severity: warning · Category: reactivity (2xxx)
Reported by: the compiler. A warning.
#What it means
{items().map((item) => <li key={item.id}>…</li>)} compiles to a keyed list: the compiler moves
the key into a key function, (item) => item.id, that the list calls for every item. The key
function runs outside the row, so it gets only what the row callback receives: the item and the
index. A key compiles when it reads only:
- the item parameter, or the bindings of its destructuring pattern (
({ id }) => <li key={id}>); - the index parameter (
(item, i) => <li key={`${i}:${item.id}`}>); - bindings it declares itself, such as a nested arrow's parameters
(
key={row.map((p) => p.id).join("|")}); consts at the top of the callback that copy item fields without calls (const id = item.id,const { id } = item), which the compiler inlines;- anything declared outside the callback.
A callback may return in several places, as long as every element it returns carries the same
key (if (!item.visible) return <li key={item.id} hidden />; return <li key={item.id}>…</li>).
The warning reports a key the compiler cannot use:
- a key that reads another value computed in the callback (
const label = format(item.name)thenkey={label}); - returned elements with different keys;
- a constant key (
key="row"), which every row would share; - a
keyon an element the callback does not return: inside a conditional (item.done ? <A key={item.id} /> : <B key={item.id} />), a fragment, or a child of the row.
Without a key, the list matches rows by reference: a row is kept only while its item is the same object, so new objects (a refetch, re-derived row data) rebuild their rows.
#Example
export function Products(props: { items: Product[] }) {
return (
<ul>
{props.items.map((item) => {
const slug = slugify(item.name);
return <li key={slug}>{item.name}</li>;
})}
</ul>
);
}#How to fix it
Compute the key from the item and the index, in the key itself, or call a function declared
outside the callback:
<ul>
{props.items.map((item) => (
<li key={slugify(item.name)}>{item.name}</li>
))}
</ul>For a conditional row, return one element with the key and move the condition inside it:
<ul>
{items().map((item) => (
<li key={item.id}>{item.done ? <Done item={item} /> : <Todo item={item} />}</li>
))}
</ul>