Skip to content
This page has been auto-translated and may contain errors.View in English

Listas e chaves

Imagine que você está construindo uma lista de tarefas: alguns itens, cada um com seu próprio texto, extraídos de um array de objetos todo. React não tem um componente especial para transformar esse array em markup. Você usa o map do JavaScript puro para transformar cada item em um pedaço de JSX, e React renderiza o resultado.

jsx
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  )
}

Renderizando uma lista com map

Esse array todos geralmente chega como uma prop passada por um componente pai que possui os dados reais. todos.map percorre o array e retorna um <li> para cada todo. As chaves em volta dele colocam esse array de elementos JSX direto no <ul>, da mesma forma que você colocaria uma única expressão. Cada <li> também recebe uma prop key, definida como todo.id. Essa parte não é opcional se você quer que a lista se comporte corretamente: se pular isso, React ainda renderiza a lista, mas registra um aviso no console, e os bugs de reordenação descritos abaixo se tornam riscos reais na primeira vez que a lista mudar de forma.

Uma chave é como React diferencia os itens da lista de uma renderização para a próxima. Ela precisa ser única entre seus irmãos, para que nenhum dos dois elementos <li> nessa lista compartilhem uma chave, e precisa ser estável, o que significa que o mesmo todo recebe a mesma chave sempre que a lista é renderizada novamente. Um id dos seus dados, como todo.id, é exatamente isso: pertence ao todo, não à sua posição no array, então permanece no lugar mesmo se a lista ao redor mudar.

Por que o índice do array é uma chave ruim

É por isso que o índice do array é uma chave ruim. É tentador, já que todo array já tem um:

jsx
{todos.map((todo, index) => (
  <li key={index}>{todo.text}</li>
))}

Isso funciona bem enquanto a lista nunca for reordenada e nunca tiver itens inseridos ou deletados. No momento em que isso acontecer, o índice para de apontar para o item ao qual costumava apontar. Delete o primeiro todo e cada item restante sobe um índice, então React vê as mesmas chaves anexadas a todos diferentes. Se algum desses itens da lista tiver seu próprio state, como uma checkbox em edição ou um input que alguém está digitando, esse state permanece anexado ao índice e acaba na linha errada. Trate o índice como um fallback para listas que são estáticas e nunca são reordenadas. Use um id real em todos os outros lugares.

Uma boa chave vem dos dados, não da renderização. Se seus todos vêm de uma API ou banco de dados, quase certamente já carregam um id: use-o diretamente em vez de derivar algo novo. Uma chave só precisa ser única entre os irmãos produzidos por aquela chamada de map, não em todo seu app, então uma lista de tarefas e uma lista de itens concluídos construídas a partir dos mesmos dados podem ambas usar todo.id como chave sem conflito. React só compara chaves dentro de um único conjunto de filhos por vez.

A chave também precisa ficar no elemento que map retorna diretamente, não em algo aninhado dentro dele:

jsx
// errado: a chave fica em um elemento interno, então React nunca a vê
{todos.map(todo => (
  <li>
    <span key={todo.id}>{todo.text}</span>
  </li>
))}

// correto: a chave fica no elemento mais externo que o callback retorna
{todos.map(todo => (
  <li key={todo.id}>{todo.text}</li>
))}

React lê chaves do elemento de nível superior de cada item no array. Uma chave enterrada dentro de um elemento filho não conta, e você ainda receberá o aviso "each child in a list should have a unique key" mesmo que uma chave exista em algum lugar do JSX.

O índice também nem sempre está errado. Para uma lista que é renderizada uma vez e nunca é reordenada, filtrada ou modificada, um conjunto de links estáticos de rodapé, por exemplo, key={index} funciona bem porque o índice e o item ao qual aponta nunca se afastam. O problema começa no momento em que essa lista pode mudar de forma: reordenando linhas, filtrando um resultado de busca conforme alguém digita, deletando um item. Nesse ponto, o índice começa a apontar para dados diferentes do que apontava na renderização anterior, e é aí que state e nós DOM acabam anexados à linha errada.

Às vezes não há um id nos dados, digamos um array simples de strings ou um array construído a partir de uma computação sem um identificador natural. Nesse caso, construa uma chave composta a partir de qualquer combinação de campos que seja realmente estável e única para essa lista, como ${todo.category}-${todo.text}, em vez de usar o índice como padrão.

Chaves são o que faz a reconciliação do React funcionar corretamente em uma lista. Quando um componente é renderizado novamente, React compara a nova lista de elementos com a antiga para descobrir o conjunto mínimo de mudanças de DOM necessárias, e usa a chave para corresponder elementos nessa comparação. A mesma chave em ambas as renderizações significa que React a trata como o mesmo elemento: ela o atualiza no lugar e mantém seu nó DOM, seu state interno e qualquer coisa anexada a ela. Nenhuma chave correspondente na renderização antiga significa que React a trata como nova e a monta do zero. Uma chave que desaparece entre renderizações significa que React desmonta esse elemento e descarta seu state.

É isso que realmente quebra quando a chave está errada. Digamos que um item de lista escondido por trás de uma chave de índice tenha seu próprio state, uma flag "expandido" em uma linha de accordion, por exemplo. Reordene o array subjacente sem mudar as chaves, e React ainda corresponde o índice antigo 2 ao novo índice 2. Ele vê "o mesmo elemento", reutiliza aquele nó DOM e seu state, e passa a flag expandida para qualquer todo que agora esteja no índice 2. Nada quebra, nada o avisa no console. As linhas silenciosamente mostram o state errado, e quem está debugando raramente desconfia que é um problema de chave no início. Um id estável contorna isso porque se move com os dados, não com a posição no array, então a reconciliação corresponde o elemento certo ao item certo não importa como a lista é reordenada.

JunoChaves dizem ao React qual item é qual Quando você transforma um array em uma lista de elementos com map, dê a cada um uma key, e use um id real dos seus dados, não sua posição no array.

A chave é como React acompanha qual elemento é qual entre renderizações, e um id que pertence ao item permanece correto mesmo se a lista for reordenada.

JunoChaves dizem ao React qual item é qual Renderizar uma lista é array.map retornando JSX, com uma key no elemento mais externo de cada item. Use um id estável dos seus dados.

O índice do array parece um atalho, mas quebra assim que a lista pode ser reordenada, inserir ou deletar, porque o índice não alinha mais com o mesmo item subjacente, e o state pode acabar anexado à linha errada.

JunoChaves dizem ao React qual item é qual Chaves são a identidade que React usa para reconciliar uma lista entre renderizações: mesma chave, mesmo elemento, state e nó DOM levados adiante; chave ausente, desmonta.

Uma chave de índice é apenas segura para uma lista que nunca muda. Qualquer coisa que seja reordenada, inserida ou deletada precisa de um id real e estável, ou você terá state silenciosamente anexado à linha errada sem nenhum erro para apontá-lo.

A seguir: State, onde um componente começa a lembrar de coisas entre renderizações.