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

Última atualização: 2026-08-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

▶ 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

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

(1) Uso básico

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

JSX
const fruits = ['Apple', 'Banana', 'Orange', 'Grapes']

function FruitList() {
  return (
    <ul>
      {fruits.map(fruit => (
        <li>{fruit}</li>
      ))}
    </ul>
  )
}
// Output:
// <ul>
//   <li>Apple</li>
//   <li>Banana</li>
//   <li>Orange</li>
//   <li>Grapes</li>
// </ul>
▶ Experimente

(2) Matriz de objetos de renderização

JSX
function ScoreTable() {
  const students = [
    { id: 1, name: 'Alice', score: 92 },
    { id: 2, name: 'Bob', score: 85 },
    { id: 3, name: 'Charlie', score: 78 }
  ]

  return (
    <table border="1" cellPadding="8">
      <thead>
        <tr><th>Name</th><th>Fractions</th><th>Level</th></tr>
      </thead>
      <tbody>
        {students.map(s => (
          <tr key={s.id}>
            <td>{s.name}</td>
            <td>{s.score}</td>
            <td>{s.score >= 90 ? 'Excellent' : s.score >= 80 ? 'Good' : 'Passing Grade'}</td>
          </tr>
        ))}
      </tbody>
    </table>
  )
}
▶ 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]
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

JSX
// ============================================
// Example:key Impact on the Input Field's State
// ============================================

function KeyDemo() {
  const [items, setItems] = React.useState([
    { id: 'a', text: 'Task A' },
    { id: 'b', text: 'Task B' },
    { id: 'c', text: 'Task C' }
  ])

  function shuffle() {
    setItems([...items].sort(() => Math.random() - 0.5))
  }

  return (
    <div>
      <button onClick={shuffle}>Randomize the order</button>
      <p>✅ has  key(id Stable,The content in the input field remains intact):</p>
      <ul>
        {items.map(item => (
          <li key={item.id}>
            {item.text} <input placeholder="Enter content" />
          </li>
        ))}
      </ul>
      
      <p>❌ Without key(or  index As key,The text in the input field appears jumbled):</p>
      <ul>
        {items.map((item, index) => (
          <li key={index}>
            {item.text} <input placeholder="The content may be misaligned" />
          </li>
        ))}
      </ul>
    </div>
  )
}
// Click"Randomize the order"after ,has  key The content in the list input field remains in order
// use  index List of,The content in the input field and the text are misaligned
▶ Experimente

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>
  )
}


7. Exemplo completo: categorias de produtos e pesquisa

JSX
// ============================================
// Complete Example:Product List(Conditional Rendering + Comprehensive Guide to List Rendering)
// Features:Filter by Category、Search、Prompt for empty results
// ============================================

function ShopPage() {
  const [products] = React.useState([
    { id: 1, name: 'Wireless Mouse', category: 'Electronics', price: 89, sales: 2000 },
    { id: 2, name: 'Mechanical Keyboard', category: 'Electronics', price: 299, sales: 1500 },
    { id: 3, name: 'Notebook', category: 'Office', price: 15, sales: 5000 },
    { id: 4, name: 'Office Chair', category: 'Furniture', price: 899, sales: 800 },
    { id: 5, name: 'Desk Lamp', category: 'Furniture', price: 129, sales: 3000 },
    { id: 6, name: 'Monitor', category: 'Electronics', price: 1599, sales: 600 }
  ])
  const [search, setSearch] = React.useState('')
  const [category, setCategory] = React.useState('All')

  // Filtering Logic
  const filtered = products.filter(p => {
    const matchSearch = p.name.includes(search)
    const matchCategory = category === 'All' || p.category === category
    return matchSearch && matchCategory
  })

  // Get the list of categories(Remove duplicates)
  const categories = ['All', ...new Set(products.map(p => p.category))]

  return (
    <div style={{ maxWidth: '800px', margin: '0 auto' }}>
      <h2>🛒 Product List</h2>
      
      {/* Search box */}
      <input
        value={search}
        onChange={e => setSearch(e.target.value)}
        placeholder="Search by Product Name..."
        style={{ width: '100%', padding: '8px', marginBottom: '12px', border: '1px solid #d9d9d9', borderRadius: '4px' }}
      />

      {/* Category Button(List Rendering) */}
      <div style={{ marginBottom: '12px' }}>
        {categories.map(cat => (
          <button
            key={cat}
            onClick={() => setCategory(cat)}
            style={{
              padding: '6px 16px', margin: '0 4px',
              backgroundColor: category === cat ? '#1890ff' : '#f0f0f0',
              color: category === cat ? 'white' : '#333',
              border: 'none', borderRadius: '4px', cursor: 'pointer'
            }}
          >
            {cat}
          </button>
        ))}
      </div>

      {/* Results Summary */}
      {search || category !== 'All' ? (
        <p style={{ color: '#666' }}>
          {search && `Search"${search}" `}
          {category !== 'All' && `/${category} `}
          Total  {filtered.length} Items
        </p>
      ) : null}

      {/* Empty result */}
      {filtered.length === 0 ? (
        <div style={{ textAlign: 'center', padding: '40px', color: '#999' }}>
          <p>😕 No matching products were found.</p>
          <button
            onClick={() => { setSearch(''); setCategory('All') }}
            style={{ padding: '8px 16px', marginTop: '8px', cursor: 'pointer' }}
          >
            Clear All Filters
          </button>
        </div>
      ) : (
        /* Product Grid(List Rendering) */
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '16px' }}>
          {filtered.map(product => (
            <div key={product.id} style={{ border: '1px solid #eee', borderRadius: '8px', padding: '16px' }}>
              <div style={{ height: '120px', backgroundColor: '#f5f5f5', borderRadius: '4px', marginBottom: '8px' }} />
              <h4>{product.name}</h4>
              <p style={{ color: '#ff4d4f', fontWeight: 'bold', fontSize: '18px' }}>${product.price}</p>
              <p style={{ color: '#999', fontSize: '13px' }}>Sold {product.sales}  items</p>
              <span style={{ background: '#f0f5ff', color: '#1890ff', padding: '2px 8px', borderRadius: '4px', fontSize: '12px' }}>
                {product.category}
              </span>
            </div>
          ))}
        </div>
      )}
    </div>
  )
}

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 📖 Somente leitura
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>
  )
}
49 linhas de lógica (limite de 40, somente leitura)

▶ Exemplo 3: Pesquisar, filtrar e ordenar listas

JSX
function FilterableList() {
  const [items] = useState([
    { id: 1, name: 'React', category: 'Frontend', stars: 220 },
    { id: 2, name: 'Django', category: 'Backend', stars: 78 },
    { id: 3, name: 'Vue', category: 'Frontend', stars: 207 },
    { id: 4, name: 'Express', category: 'Backend', stars: 63 },
    { id: 5, name: 'Tailwind', category: 'CSS', stars: 80 },
  ])
  const [search, setSearch] = useState('')
  const [sortKey, setSortKey] = useState('name')

  const filtered = items
    .filter(i => i.name.toLowerCase().includes(search.toLowerCase()))
    .sort((a, b) => sortKey === 'stars' ? b.stars - a.stars : a.name.localeCompare(b.name))

  return (
    <div style={{ maxWidth: 400, margin: '0 auto' }}>
      <input value={search} onChange={e => setSearch(e.target.value)}
        placeholder="Search frameworks..." style={{ width: '100%', padding: 8, borderRadius: 4, marginBottom: 8 }} />
      <div style={{ marginBottom: 8 }}>
        <button onClick={() => setSortKey('name')} style={{ padding: '4px 12px', marginRight: 4, cursor: 'pointer', background: sortKey === 'name' ? '#1890ff' : '#f0f0f0', color: sortKey === 'name' ? 'white' : '#333', border: 'none', borderRadius: 4 }}>A-Z</button>
        <button onClick={() => setSortKey('stars')} style={{ padding: '4px 12px', cursor: 'pointer', background: sortKey === 'stars' ? '#1890ff' : '#f0f0f0', color: sortKey === 'stars' ? 'white' : '#333', border: 'none', borderRadius: 4 }}>Stars</button>
      </div>
      {filtered.length === 0 ? (
        <p style={{ color: '#999', textAlign: 'center' }}>No results</p>
      ) : (
        <ul style={{ listStyle: 'none', padding: 0 }}>
          {filtered.map(i => <li key={i.id} style={{ padding: 8, borderBottom: '1px solid #f0f0f0', display: 'flex', justifyContent: 'space-between' }}><span>{i.name} <small style={{ color: '#999' }}>({i.category})</small></span><span>⭐ {i.stars}k</span></li>)}
        </ul>
      )}
    </div>
  )
}
▶ Experimente

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

JSX
function CategorizedProducts() {
  const categories = [
    { id: 'electronics', name: 'Electronics', products: [
      { id: 1, name: 'Keyboard', price: 79 },
      { id: 2, name: 'Monitor', price: 399 },
    ]},
    { id: 'furniture', name: 'Furniture', products: [
      { id: 3, name: 'Desk', price: 249 },
      { id: 4, name: 'Chair', price: 189 },
    ]},
    { id: 'books', name: 'Books', products: [] },
  ]

  return (
    <div style={{ maxWidth: 500, margin: '0 auto' }}>
      {categories.map(cat => (
        <div key={cat.id} style={{ marginBottom: 16 }}>
          <h3 style={{ borderBottom: '2px solid #1890ff', paddingBottom: 4 }}>{cat.name}</h3>
          {cat.products.length === 0 ? (
            <p style={{ color: '#999', fontStyle: 'italic', paddingLeft: 16 }}>No products yet</p>
          ) : (
            <ul style={{ listStyle: 'none', padding: 0, paddingLeft: 16 }}>
              {cat.products.map(p => (
                <li key={p.id} style={{ padding: 4, display: 'flex', justifyContent: 'space-between' }}>
                  <span>{p.name}</span><span>${p.price}</span>
                </li>
              ))}
            </ul>
          )}
        </div>
      ))}
    </div>
  )
}
▶ Experimente

❓ Perguntas Frequentes

P: Quando exatamente se deve usar && e ? :? **R: && é usado para “exibir ou não exibir” (sem ramificação else), como “exibir um ponto vermelho somente quando houver uma notificação”; ? : é usado para “exibir A ou exibir B” (duas ramificações), como “exibir verde quando estiver online, exibir vermelho quando estiver offline”. Use && quando não houver ramificação “else” e use ? : quando houver uma ramificação “else”.

P: Por que {items.length && <List />} exibe 0? R: Quando items.length é 0, o resultado de 0 && <List /> é 0 (avaliação por curto-circuito). O React exibe esse 0 na página. Sempre use {items.length > 0 && <List />}.

P: Qual é exatamente o problema em usar index como chave? **R: Há três problemas: ① Após ordenar ou filtrar uma lista, o index permanece inalterado, mas os dados mudam, fazendo com que o React reutilize os elementos DOM errados e resultando em inconsistências de estado (como conteúdo desalinhado nos campos de entrada); ② Ao inserir elementos no início da lista, todos os valores de index mudam, exigindo que todo o DOM seja reconstruído; ③ Quando elementos são removidos, o index avança, o que também pode causar inconsistências. Quando não houver IDs estáveis, use pelo menos item.text + Math.random() ou uuid() para gerar chaves únicas.

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%