Menu

Listas e keys no React: map(), a prop key e o índice

Para renderizar uma lista no React você chama map() em um array e retorna JSX para cada item, dando a cada item uma prop key. Aprenda a filtrar e depois mapear, de onde as keys devem vir e por que keys de índice misturam os itens quando a lista muda.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

Para renderizar uma lista no React, chame map() em um array e retorne um trecho de JSX para cada item. Dê ao elemento mais externo de cada item uma prop key com um valor que identifique esse item, normalmente o ID dele, para que o React consiga diferenciar os itens quando a lista muda.

Adicione { id: 'dat', name: 'Date', price: 4 } ao array e aparece uma quarta linha. O JSX não tem uma sintaxe de loop própria: map retorna um array de elementos, e o React renderiza um array renderizando cada elemento em ordem.

Renderizando arrays com map

map chama sua função uma vez por item e junta os resultados em um novo array. Dentro das chaves do JSX esse array é só um valor, então você pode mapear direto na marcação, como acima, ou montar o array antes:

const items = fruits.map((fruit) => <li key={fruit.id}>{fruit.name}</li>);
return <ul>{items}</ul>;

Dois detalhes costumam pegar as pessoas. Se você usa chaves na arrow function, precisa de um return: fruits.map((f) => { return <li key={f.id}>{f.name}</li>; }). Sem ele a função retorna undefined e a lista fica vazia. E a key vai no elemento retornado pelo map, não em um elemento dentro dele. Se o item é um componente, coloque a key no componente: <FruitRow key={fruit.id} fruit={fruit} />.

Filtrar, depois mapear

Para mostrar só alguns itens, filtre o array primeiro e mapeie o resultado. Cada método faz uma coisa só, e a cadeia se lê como a frase "as frutas que combinam, como itens de lista".

Digite er na busca e a lista se reduz a Eraser, Ruler e Stapler; marque In stock only e o Ruler também sai. A lista filtrada é calculada durante a renderização a partir das duas partes de estado, então não existe um segundo array no estado para manter sincronizado. visible.length === 0 && é um uso seguro do && porque a comparação é um booleano de verdade (a página sobre renderização condicional mostra o que acontece com um 0 solto).

O que as keys fazem

Quando uma lista renderiza de novo, o React compara o novo array de elementos com o anterior. As keys são como ele os associa: o elemento com key 3 agora é o elemento com key 3 de antes, mesmo que tenha mudado de lugar. O React mantém o nó DOM e o estado do componente desse item e só move ou atualiza o que mudou. Itens com keys novas são criados, e keys que sumiram são removidas.

Sem keys, a única coisa pela qual o React consegue associar é a posição. Isso funciona enquanto nada se move, e dá errado assim que algo se move.

Por que keys de índice quebram

O map passa o índice como segundo argumento, então key={index} é tentador. O índice descreve uma posição, não um item. Insira um item no topo e o índice de todos os itens avança uma posição, então o React acredita que o item 0 continua sendo o item 0 e mantém ali o estado antigo.

A demonstração abaixo renderiza a mesma lista duas vezes, uma com key de índice e outra com key de ID. Cada linha tem um input que guarda o próprio texto.

Digite math ao lado de Ada nas duas listas e depois clique em Add to top. Na lista por ID, a anotação continua com Ada. Na lista por índice, a anotação pula para a pessoa nova, porque o primeiro input ainda é o input da posição 0, e a posição 0 agora é outra pessoa. Os nomes estão certos nas duas listas, já que vêm dos dados; só o estado que vive no DOM e nos componentes dá errado. Apps reais esbarram nisso com checkboxes, foco, animações e qualquer componente que tenha useState.

Keys de índice só são seguras quando tudo isto vale: a lista nunca é reordenada nem filtrada, itens nunca são inseridos ou removidos a não ser no final, e os itens não têm estado próprio. Uma lista estática de links de rodapé se encaixa. Qualquer coisa que um usuário possa editar normalmente não.

De onde vêm as keys

A melhor key é um ID que já está nos dados: um ID do banco de dados, o SKU de um produto, um slug, um nome de usuário. Dados vindos de um servidor quase sempre têm um.

Para itens criados no navegador, dê a cada item um ID quando criá-lo e guarde-o junto com o item: um contador, como na demonstração acima, ou crypto.randomUUID(). Nunca gere a key durante a renderização:

// Wrong: a new key on every render, so React remounts every item every time
{todos.map((todo) => <Todo key={Math.random()} todo={todo} />)}

// Right: the ID is created once, in the event that adds the item
function addTodo(text) {
    setTodos([...todos, { id: crypto.randomUUID(), text }]);
}

Uma key que muda a cada renderização é pior que um índice: todos os itens são destruídos e recriados toda vez, os inputs perdem o foco enquanto você digita e nada mantém o estado. O mesmo truque é útil de propósito: mudar a key de um componente o reinicia, o que a página sobre useState usa para limpar um formulário.

As keys só precisam ser únicas entre irmãos

Uma key precisa ser única dentro de uma lista, não no app inteiro. Duas listas separadas podem reutilizar os mesmos IDs, porque o React só compara keys entre os filhos do mesmo pai.

As duas seções têm pratos com keys 1 e 2, e isso está correto: cada ul é uma lista própria. O problema são keys duplicadas dentro de uma lista: o React não consegue diferenciar os itens em conflito e, quando a lista atualiza, pode duplicá-los ou descartá-los.

Quando um item renderiza vários elementos irmãos, como um <dt> e um <dd>, envolva-os em <Fragment key={item.id}>. A sintaxe curta <> não aceita key.

O aviso "unique key prop"

Se você deixa a key de fora, o React imprime isto no console do navegador durante o desenvolvimento:

Each child in a list should have a unique "key" prop.

Check the render method of `App`.

A segunda linha nomeia o componente cuja renderização produziu a lista, o que diz qual map corrigir. Dois irmãos com a mesma key geram uma mensagem diferente, que começa com "Encountered two children with the same key". A prévia desta página roda como um build de produção, que pula essa verificação, então você não vai ver o aviso aqui mesmo que apague uma key. Apague uma mesmo assim no primeiro exemplo: a lista continua renderizando, porque uma key ausente é um risco de correção, não um travamento. Para corrigir o aviso, adicione uma key estável ao elemento retornado pelo map; adicionar key={index} o silencia, mas traz de volta o bug mostrado acima.

Repare também que a key não é passada para o seu componente. Dentro de FruitRow, props.key é undefined. Se o componente precisa do ID, passe-o uma segunda vez como uma prop normal: <FruitRow key={f.id} id={f.id} />.

Perguntas frequentes

Por que o React precisa de uma key nos itens de lista?

As keys dizem ao React qual item é qual entre renderizações. Quando o array muda, o React associa os itens antigos e novos pela key, e assim consegue manter o estado e o nó DOM de cada item com os dados certos em vez de adivinhar pela posição.

Posso usar o índice do array como key?

Só em uma lista que nunca muda de ordem e nunca tem itens inseridos ou removidos no meio. Se os itens podem se mover, uma key de índice amarra o estado à posição, e o texto digitado, os checkboxes e o foco acabam no item errado.

O que devo usar como key?

Um ID que pertença aos dados: um ID do banco de dados, o SKU de um produto, um nome de usuário. Para itens criados no navegador, gere um ID uma vez quando criar o item (um contador ou crypto.randomUUID()), nunca durante a renderização.

As keys precisam ser únicas no app inteiro?

Não. As keys só precisam ser únicas entre irmãos na mesma lista. Duas listas diferentes podem usar as mesmas keys.

Um componente consegue ler a própria key?

Não. key é usada pelo React e não é passada para o componente como prop. Se o componente precisa do ID, passe-o de novo com outro nome, como id={item.id}.

Como corrijo "Each child in a list should have a unique key prop"?

Adicione uma key ao elemento mais externo que você retorna do map, usando um ID estável do item. Se esse elemento é um fragment, use <Fragment key={id}>, já que <> não aceita key.

Ilustração das linguagens de programação do Coddy

Aprenda a programar com o Coddy

COMEÇAR