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
- Use o operador ternário
? :para controlar o conteúdo exibido - Use
&&e||para simplificar a renderização condicional - Exibir a lista usando
.map() - A importância do atributo “key” e as regras para seu uso
- Tratamento dos estados “vazio” e “carregando”
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:
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
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>
)
}
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”:
function StatusBadge({ isActive }) {
return (
<span style={{ color: isActive ? '#52c41a' : '#ff4d4f' }}>
{isActive ? '🟢 Online' : '🔴 Offline'}
</span>
)
}
// isActive=true → <span>🟢 Online</span>
// isActive=false → <span>🔴 Offline</span>
(2) Lógica e &&
Adequado para “Exibir somente se as condições forem atendidas; caso contrário, não exibir”:
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"
{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”:
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>
)
}
Saída:
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
// ============================================
// 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>
)
}
Saída:
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:
(2) Matriz de objetos de renderização
▶ Experimente(3) 3 técnicas de transformação de dados para a exibição de listas
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)}
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.
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:
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
▶ ExperimenteSaída:
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:
// ============================================
// 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
▶ Expandir exemplo executável (81 linhas)
Saída:
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
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>
)
}
Saída:
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:
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
▶ ExperimenteSaída:
Frameworks: React ⭐220k, Vue ⭐207k, Tailwind ⭐80k, Django ⭐78k, Express ⭐63k. Busca com filtro; alternância de ordenação A-Z/Stars.
Saída:
Exibe "{cat.name}"
▶ Exemplo 4: Exibição de listas aninhadas — Exibição de produtos categorizados
▶ ExperimenteSaída:
Electronics: Keyboard $79, Monitor $399
Furniture: Desk $249, Chair $189
Books: "No products yet" (estado vazio em itálico)
❓ Perguntas Frequentes
.map() pode ser aninhado? Como as chaves são tratadas ao usar várias chamadas de .map()?.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).&& é 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
- Existem três tipos de renderização condicional:
? :(uma ou outra),&&(apenas exibição) e||(valor alternativo) - Use
.map()para renderizar a lista; cada elemento JSX deve estar vinculado a umkeyexclusivo - Os projetos reais devem lidar com três estados: carregando, falha no carregamento e sem dados
- Cuidado com a armadilha de renderização 0 de
{items.length && <List />} - Use um identificador único e estável como chave; não use um índice
📝 Exercícios
- Problema básico (Dificuldade ⭐): Crie um componente
TemperatureDisplayque, com base nos props dotemp, 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. - Problema avançado (Dificuldade ⭐⭐): Crie um componente
TodoFilterque aceite duas propriedades —todos(uma matriz de tarefas) efilter(todas/concluídas/não concluídas) — e implemente uma filtragem com base no estado. - Desafio (Dificuldade: ⭐⭐⭐): Crie um componente
Paginationque aceite os propstotalPages,currentPageeonPageChangee exiba botões de número de página, incluindo a primeira e a última página, bem como uma elipse.