React: Renderização condicional e renderização de listas

Última atualização: 2026-10-26

A renderização condicional é como sinais de trânsito — ela determina o que exibir com base no estado; a renderização de lista é como produção em linha de montagem — uma lista de dados que gera componentes de interface do usuário idênticos em lotes.


1. O que você vai aprender



2. A história do front-end de uma plataforma de comércio eletrônico

(1) Desafio: Os 6 estágios de uma lista de produtos

Alice está trabalhando em uma página de lista de produtos para uma plataforma de comércio eletrônico. Essa página possui seis estados de exibição e, cada vez que o estado muda, ela precisa manipular manualmente o DOM:

JAVASCRIPT
function renderProductList(state) {
  const container = document.getElementById('product-list')
  container.innerHTML = ''

  if (state.isLoading) {
    container.innerHTML = '<div class="loading">Loading......</div>'
  } else if (state.error) {
    container.innerHTML = '<div class="error">Failed to load</div>'
  } else if (!state.isLoggedIn) {
    container.innerHTML = '<div>Please log in</div>'
  } else if (state.products.length === 0) {
    container.innerHTML = '<div>No products available</div>'
  } else {
    let html = ''
    state.products.forEach(p => {
      html += `<div class="card"><h3>${p.name}</h3><p>$${p.price}</p></div>`
    })
    container.innerHTML = html
  }
}

Pergunta da Alice: Sempre que o estado muda, preciso limpar manualmente o contêiner e construir o HTML. São 30 linhas de código que fazem apenas uma coisa: exibir o estado atual.

(2) Soluções para renderização condicional no React

JSX
function ProductList({ user, products, isLoading, error }) {
  // Each state is independent if Branch,No interference with one another
  if (isLoading) return <LoadingSpinner />
  if (error) return <ErrorMessage message={error} />
  if (!user) return <LoginPrompt />
  if (products.length === 0) return <EmptyState />
  
  return (
    <div className="product-grid">
      {products.map(product => (
        <ProductCard key={product.id} product={product} />
      ))}
    </div>
  )
}
▶ Experimente

Vantagens: Apenas 15 linhas de código abrangem todos os casos, e cada ramificação é fácil de entender à primeira vista.



3. Três maneiras de usar a renderização condicional

Método Sintaxe Casos de uso Observações
Operador ternário condition ? A : B Escolha uma das duas opções (com um ramo “else”) Operadores ternários aninhados são difíceis de ler; evite mais de dois níveis
Lógica e && condition && A Exibir somente se a condição for atendida (sem “else”) 0 && <Comp /> exibirá 0; use > 0
OU lógico ` ` `value

(1) Operador ternário ? :

Cenários adequados para “escolher uma das duas opções”:

JSX
function StatusBadge({ isActive }) {
  return (
    <span style={{ color: isActive ? '#52c41a' : '#ff4d4f' }}>
      {isActive ? '🟢 Online' : '🔴 Offline'}
    </span>
  )
}

// isActive=true  → <span>🟢 Online</span>
// isActive=false → <span>🔴 Offline</span>
▶ Experimente

(2) Lógica e &&

Adequado para “Exibir somente se as condições forem atendidas; caso contrário, não exibir”:

JSX
function Notification({ count }) {
  return (
    <div className="notification-icon">
      🔔
      {/* count > 0 A red dot appears,Otherwise, nothing will be displayed. */}
      {count > 0 && (
        <span className="badge">{count > 99 ? '99+' : count}</span>
      )}
    </div>
  )
}

// count=0  → Show only 🔔
// count=5  → Show 🔔 and  "5"
▶ Experimente
⚠️ Armadilha: Não escreva {items.length && <List />}. Quando items.length é 0, o resultado de 0 && <List /> é 0, e o React exibirá 0 na página. A maneira correta de escrever isso é {items.length > 0 && <List />}.

(3) OU lógico ||

Adequado para “fornecer um valor alternativo”:

JSX
function UserProfile({ user }) {
  return (
    <div>
      <h2>{user.nickname || 'Anonymous User'}</h2>
      <p>{user.bio || 'This person is lazy.,Nothing was written...'}</p>
      <p>Location:{user.location || 'Unknown'}</p>
    </div>
  )
}
▶ Experimente

Saída:

TEXT
Comparação lado a lado: sintaxe HTML vs sintaxe JSX mostrando marcação equivalente. Componente React com tipagem TypeScript usando interface para as props

▶ Exemplo: Comparação lado a lado de três tipos de renderização condicional

JSX
// ============================================
// Example:Combined Use of Three Types of Conditional Rendering in the User Panel
// ============================================

function UserPanel({ user }) {
  return (
    <div className="panel">
      {/* 1. Ternary:Choose one of the two */}
      <h2>
        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}
      </h2>

      {/* 2. &&:Display only when met */}
      {user.isVIP && (
        <p>Membership Expiration:{user.vipExpireDate}</p>
      )}

      {/* 3. ||:Floor value */}
      <p>Signature:{user.slogan || 'This person is really cool.,No signature'}</p>

      {/* Combined Use:List of Tags */}
      {user.tags && user.tags.length > 0 && (
        <div>
          {user.tags.map(tag => (
            <span key={tag} style={{ background: '#f0f0f0', padding: '2px 8px', margin: '2px', borderRadius: '4px' }}>
              {tag}
            </span>
          ))}
        </div>
      )}
    </div>
  )
}
▶ Experimente

Saída:

TEXT
Se for VIP: "⭐ Premium Member" + expiração da assinatura. Caso contrário: "Regular User". Assinatura: texto do usuário ou conteúdo padrão. As tags só são exibidas quando presentes.


4. Renderização de listas: .map()

(1) Uso básico

Array.map() Percorrer um array e retornar um JSX para cada elemento:

▶ Experimente

(2) Matriz de objetos de renderização

▶ Experimente

(3) 3 técnicas de transformação de dados para a exibição de listas

JSX
const items = [
  { id: 1, name: 'React Basics', price: 0 },
  { id: 2, name: 'Vue Getting Started', price: 0 },
  { id: 3, name: 'Node.js Real-World Experience', price: 99 }
]

// 1. Direct Rendering
{items.map(item => <li key={item.id}>{item.name}</li>)}

// 2. Filter First, Then Render(Free Courses)
{items
  .filter(item => item.price === 0)
  .map(item => <li key={item.id}>{item.name}(Free)</li>)
}

// 3. Extract Rendering Functions
function renderItem(item) {
  return (
    <li key={item.id}>
      {item.name} - ${item.price}
    </li>
  )
}
{items.map(renderItem)}
▶ Experimente

5. A importância da chave

O React usa key para identificar cada elemento de uma lista. Quando a chave permanece constante, o React reutiliza o nó do DOM; quando a chave muda, o React o destrói e o recria.

100%
graph LR
    A[List Changes] --> B{key Changes?}
    B -->|No, key The same| C[Reuse DOM Node<br/>Update only the changes]
    B -->|Yes, key It's changed| D[Delete Old Nodes<br/>Create a New Node]
    B -->|No, None key| E[Render All Over]

Saída:

TEXT
Subtítulo: "        {user.isVIP ? '⭐ Premium Member' : 'Regular User'}      ". Exibe: "0 && ("
Estratégia-chave Desempenho Recomendação
ID exclusivo do banco de dados ✅ Ideal ⭐⭐⭐
Sequência única (UUID) ✅ Melhor ⭐⭐⭐
Índice da matriz ⚠️ Arriscado (o estado pode ficar inconsistente quando a lista for alterada) ⭐
Número aleatório ❌ Intervalo (varia a cada renderização) ❌
Nenhuma chave passada ❌ Desempenho insatisfatório ❌

▶ Exemplo: Com chave vs. Sem chave

▶ Experimente

Saída:

TEXT
Duas listas com campos de entrada. Clique em "Randomize": ✅ a lista (key=id) mantém os valores digitados alinhados; ❌ a lista (key=index) desalinha os valores digitados em relação ao texto


6. Estado vazio e estado de carregamento

Status Condição Comportamento da interface do usuário Tratamento
Carregando loading === true Indicador de carregamento / Tela em esqueleto if (loading) return <Spinner />
Falha no carregamento error !== null Mensagem de erro + botão “Tentar novamente” if (error) return <ErrorView />
Dados em branco data.length === 0 Ilustração do estado vazio + Texto if (!data.length) return <Empty />
Normal Nenhuma das opções acima Exibir lista de dados Padrão: retornar à interface de usuário normal

Em projetos reais, há três estados que devem ser tratados ao carregar dados:

JSX
// ============================================
// Example:Complete List Component(3 Handling Different States)
// ============================================

function ArticleList() {
  const [articles, setArticles] = React.useState([])
  const [isLoading, setIsLoading] = React.useState(true)
  const [error, setError] = React.useState(null)

  // Simulated Loading(Actually, it is API Call)
  React.useEffect(() => {
    fetch('/api/articles')
      .then(res => {
        if (!res.ok) throw new Error('Network request failed')
        return res.json()
      })
      .then(data => {
        setArticles(data)
        setIsLoading(false)
      })
      .catch(err => {
        setError(err.message)
        setIsLoading(false)
      })
  }, [])

  // 1. Loading...
  if (isLoading) {
    return (
      <div style={{ textAlign: 'center', padding: '40px' }}>
        <div className="spinner" />
        <p>Loading article...</p>
      </div>
    )
  }

  // 2. Failed to load
  if (error) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#ff4d4f' }}>
        <p>❌ Failed to load:{error}</p>
        <button onClick={() => window.location.reload()}>Reload</button>
      </div>
    )
  }

  // 3. Empty data
  if (articles.length === 0) {
    return (
      <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
        <p>📭 No articles available</p>
        <p>No one has posted an article yet.,Go ahead and write your first one!!</p>
      </div>
    )
  }

  // 4. Normal Rendering
  return (
    <div>
      <h3>List of Articles(Total  {articles.length}  articles)</h3>
      {articles.map(article => (
        <ArticleCard key={article.id} article={article} />
      ))}
    </div>
  )
}
▶ Experimente

7. Exemplo completo: categorias de produtos e pesquisa

▶ Expandir exemplo executável (81 linhas)
▶ Experimente

Saída:

TEXT
Exibe: "Randomize the order". Botão: Randomize the order. Entrada: Enter content, The content may be misaligned

Resultado esperado: Uma página completa com a lista de produtos que ofereça filtragem por pesquisa, filtragem por categoria, notificações de resultados nulos e estatísticas.


▶ Exemplo 5: Renderização dinâmica de campos de formulário

JSX
function DynamicForm() {
  const [formType, setFormType] = useState('personal')
  const [formData, setFormData] = useState({
    name: '',
    email: '',
    company: '',
    taxId: '',
    budget: '',
  })

  function handleChange(e) {
    const { name, value } = e.target
    setFormData(prev => ({ ...prev, [name]: value }))
  }

  const personalFields = (
    <>
      <input name="name" value={formData.name} onChange={handleChange} placeholder="Name" />
      <input name="email" value={formData.email} onChange={handleChange} placeholder="Email" />
    </>
  )

  const businessFields = (
    <>
      <input name="company" value={formData.company} onChange={handleChange} placeholder="Company" />
      <input name="taxId" value={formData.taxId} onChange={handleChange} placeholder="Tax ID" />
      <input name="budget" value={formData.budget} onChange={handleChange} placeholder="Budget ($)" />
    </>
  )

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <h3>Registration</h3>
      <div style={{ marginBottom: 12 }}>
        {['personal', 'business'].map(type => (
          <button
            key={type}
            onClick={() => setFormType(type)}
            style={{
              padding: '6px 16px', marginRight: 8, cursor: 'pointer',
              backgroundColor: formType === type ? '#1890ff' : '#f0f0f0',
              color: formType === type ? 'white' : '#333', border: 'none', borderRadius: 4,
            }}
          >
            {type === 'personal' ? 'Personal' : 'Business'}
          </button>
        ))}
      </div>
      {formType === 'personal' ? personalFields : businessFields}
      <button style={{ marginTop: 12, padding: '8px 24px', cursor: 'pointer' }}>Submit</button>
    </div>
  )
}
▶ Experimente

Saída:

TEXT
Formulário pessoal: campos Name + Email. Formulário empresarial: campos Company + Tax ID + Budget. Alternados por botões. Botão de envio na parte inferior.

Saída:

TEXT
O campo de busca filtra os itens. Botões de ordenação: A-Z (por nome) / Stars (por avaliação). A ordenação atual é destacada em azul. A lista filtrada e ordenada é atualizada de forma reativa

▶ Exemplo 3: Pesquisar, filtrar e ordenar listas

▶ Experimente

Saída:

TEXT
Frameworks: React ⭐220k, Vue ⭐207k, Tailwind ⭐80k, Django ⭐78k, Express ⭐63k. Busca com filtro; alternância de ordenação A-Z/Stars.

Saída:

TEXT
Exibe "{cat.name}"

▶ Exemplo 4: Exibição de listas aninhadas — Exibição de produtos categorizados

▶ Experimente

Saída:

TEXT
Electronics: Keyboard $79, Monitor $399
Furniture: Desk $249, Chair $189
Books: "No products yet" (estado vazio em itálico)

❓ Perguntas Frequentes

P O .map() pode ser aninhado? Como as chaves são tratadas ao usar várias chamadas de .map()?
R Sim, ele pode ser aninhado. Cada nível de .map() deve ter sua própria chave. Por exemplo, ao renderizar “Categoria → Produto: cats.map(c => <div key={c.id}><h3>{c.name}</h3>{c.products.map(p => <span key={p.id}>{p.name}</span>)}</div>)”. Chaves em níveis diferentes podem ser iguais (desde que sejam únicas dentro de suas respectivas matrizes).
P O que é melhor — expressões ternárias ou a sintaxe &&?
R Cada uma tem seu próprio caso de uso: && é adequada para “exibir se a condição for atendida; não renderizar nada se não for”; a expressão ternária ? : é adequada para “exibir o componente X se a condição A for atendida; exibir o componente Y se a condição B for atendida”. Se você quiser “não exibir nada quando a condição não for atendida” usando o operador ternário, a sintaxe seria condition ? <Comp /> : null, mas && é mais concisa nesse caso. No entanto, esteja ciente da “armadilha da renderização de 0” com && — 0 && <Comp /> renderizará um 0.

📖 Resumo


📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Crie um componente TemperatureDisplay que, com base nos props do temp, exiba 🔥 em vermelho quando a temperatura for >35 °C, ☀️ em amarelo quando estiver entre 15 e 35 °C e ❄️ em azul quando for <15 °C.
  2. Problema avançado (Dificuldade ⭐⭐): Crie um componente TodoFilter que aceite duas propriedades — todos (uma matriz de tarefas) e filter (todas/concluídas/não concluídas) — e implemente uma filtragem com base no estado.
  3. Desafio (Dificuldade: ⭐⭐⭐): Crie um componente Pagination que aceite os props totalPages, currentPage e onPageChange e exiba botões de número de página, incluindo a primeira e a última página, bem como uma elipse.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%