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

Renderização condicional

Um visitante logado vê um dashboard, um visitante não logado vê um formulário de login, e uma requisição que falhou precisa mostrar uma mensagem de erro em algum lugar da página. Um booleano como isLoggedIn geralmente fica no state, e o que muda é qual pedaço de UI é renderizado para seu valor atual. JSX é JavaScript, então decidir o que renderizar funciona da mesma forma que qualquer outra decisão no seu código: uma expressão que escolhe um valor, avaliada bem ali na declaração return.

Escolhendo entre dois elementos com um operador ternário

Quando há exatamente duas coisas que um pedaço de UI poderia ser, o operador ternário escolhe entre elas:

jsx
return (
  <div>
    {isLoggedIn ? <Dashboard /> : <Login />}
    {hasError && <p>Algo deu errado</p>}
  </div>
)

isLoggedIn ? <Dashboard /> : <Login /> lê-se igual a qualquer outro ternário: se isLoggedIn é verdadeiro, essa expressão avalia para <Dashboard />, caso contrário avalia para <Login />. Tudo que ela avalia fica renderizado naquele lugar no JSX. As chaves são o que deixa você colocar uma expressão JavaScript no meio da marcação, e um ternário é uma expressão como qualquer outra.

Mostrando ou ocultando um elemento com &&

A segunda linha lida com uma forma diferente de decisão: mostrar algo ou não mostrar nada. hasError && <p>Algo deu errado</p> usa o operador && do jeito que funciona em qualquer outro lugar do JavaScript. Se hasError é false, && interrompe a avaliação e toda a expressão avalia para false, sem nunca chegar ao JSX da direita. Se hasError é true, a expressão avalia para o elemento <p>.

A razão disso renderizar corretamente depende do que React faz com o resultado. React não renderiza nada para false, null e undefined, então quando hasError é false, nada aparece na página.

A pegadinha dos valores falsy

&& não produz apenas true ou false. Como qualquer expressão JavaScript usando &&, ela avalia para o lado em que termina, e esse lado pode ser qualquer valor, não necessariamente um booleano. Na maioria das vezes isso é inofensivo, mas vira um bug quando o lado esquerdo é um número:

jsx
{count && <Badge />}
// count = 0 → renderiza "0" na página

Se count é 0, essa expressão avalia para 0. React não renderiza para false, null e undefined, mas 0 é um valor real e renderizável, então React o coloca na página. O badge não aparece, mas um 0 solto fica ali, bem onde você esperava nada.

A solução é garantir que o lado esquerdo de && é sempre um booleano de verdade:

jsx
{count > 0 && <Badge />}
// count = 0 → renderiza nada

count > 0 sempre avalia para true ou false, então a expressão renderiza o badge ou renderiza nada, sem deixar nenhum 0 para trás.

Não renderizando nada

Às vezes um componente não tem nada para mostrar, e a forma mais clara de dizer isso é retornar cedo:

jsx
function Banner({ message }) {
  if (!message) {
    return null
  }

  return <p className="banner">{message}</p>
}

Quando message está vazio, a função retorna null antes de construir o resto do JSX. Isso mantém o return principal focado no caso em que há algo realmente para renderizar, em vez de envolver tudo em mais uma condição.

A ferramenta a usar depende do que a condição está escolhendo entre. Dois elementos: use um ternário. Um elemento ou nada: use &&. Um componente sem nada para renderizar: retorne cedo com if (!x) return null antes do return principal, em vez de envolver toda a árvore JSX em mais uma condição. Misturá-los, um ternário onde && seria suficiente ou um retorno antecipado enterrado dentro de um ternário, é geralmente um sinal para mudar para a ferramenta mais direta.

A lição da pegadinha falsy em {count && <Badge />} não está limitada a 0. NaN renderiza do mesmo jeito, então {total / count && <Badge />} pode imprimir NaN na página se count é 0. Uma string vazia "" é a versão mais silenciosa do mesmo bug: {name && <Greeting name={name} />} não renderiza nada visível quando name é "", porque uma string vazia na página é invisível, mas ainda é um nó de texto solto no DOM. A solução é a mesma usada para 0: force o lado esquerdo para um booleano explícito, com uma comparação como count > 0 ou uma chamada Boolean(...), em vez de confiar no valor bruto ser falsy do jeito que você espera.

Condições ficam mais difíceis de ler quando há mais de uma em jogo. Um ternário aninhado dentro de outro ternário em JSX é o primeiro sinal de que uma condição cresceu demais para lógica inline:

jsx
{status === 'loading' ? <Spinner /> : status === 'error' ? <ErrorMessage /> : <Content />}

Duas opções para limpar isso. Tire a decisão para uma variável acima do return, para que o JSX só tenha que incorporar o resultado:

jsx
const view =
  status === 'loading' ? <Spinner /> :
  status === 'error' ? <ErrorMessage /> :
  <Content />

return <div>{view}</div>

Ou extraia a lógica para uma pequena função auxiliar que retorna o elemento certo, que fica mais legível uma vez que há mais de duas ou três ramificações. De qualquer forma, o objetivo é o mesmo: manter o JSX em si livre de decisões e deixá-lo incorporar valores que já foram decididos acima dele.

Retornar null de um componente não renderiza nada: nenhum nó DOM é criado para ele, nem um vazio. É um valor de retorno legítimo para um componente, e React trata um retorno null da mesma forma que trata um fragmento vazio.

Há uma segunda coisa que vale a pena saber quando você começa a trocar componentes dentro e fora do mesmo lugar. React reconcilia a árvore caminhando por ela posição por posição, e em cada posição compara o tipo de elemento que está ali agora contra o tipo que estava no render anterior.

jsx
{isEditing ? <EditForm /> : <ViewForm />}

Quando isEditing muda, EditForm e ViewForm são tipos de componente diferentes ocupando a mesma posição, então React desmonta o antigo e monta o novo do zero. Qualquer state que EditForm estava mantendo, um valor de input que o usuário estava digitando, por exemplo, desaparece no momento em que ViewForm toma seu lugar. Isso é diferente de renderizar o mesmo tipo de componente com props diferentes naquela posição: mesmo tipo na mesma posição significa que React atualiza a instância existente e seu state sobrevive. O tipo é o que determina se React vê "a mesma coisa, atualizada" ou "uma coisa completamente nova".

JunoDecidir o que mostrar é JavaScript ordinário Um ternário escolhe entre dois elementos, && mostra um elemento ou mostra nada, e retornar null de um componente é como você diz "não há nada para renderizar aqui".

Cuidado com {count && <Badge />} quando count pode ser 0, já que 0 é um valor que React vai de fato imprimir. Escrever {count > 0 && <Badge />} em vez disso mantém o lado esquerdo um booleano de verdade.

JunoDecidir o que mostrar é JavaScript ordinário Use um ternário quando você está escolhendo entre dois elementos, e && quando você está escolhendo entre um elemento e nada.

A pegadinha a lembrar com && é que ela avalia para o lado em que termina, então um número falsy como 0 fica renderizado como ele mesmo em vez de desaparecer. Proteja-se contra isso tornando o lado esquerdo um booleano explícito, como count > 0, e use um return null antecipado quando um componente não tem nada para mostrar.

JunoDecidir o que mostrar é JavaScript ordinário Todo truque de renderização condicional aqui é JavaScript ordinário avaliado dentro de JSX, e a única parte específica do React é que false, null e undefined renderizam como nada enquanto todo outro valor, incluindo 0, renderiza como ele mesmo. É isso que torna {count && <Badge />} uma armadilha e {count > 0 && <Badge />} a solução.

A parte que vale levar adiante é a reconciliação por posição e tipo: coloque um tipo de componente diferente no mesmo lugar e seu state desaparece, porque React vê um elemento novo, não uma atualização do antigo.

Próximo: Formulários, onde você vai usar state para lidar com campos de entrada.