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
- 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>
)
}
▶ 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>
)
}
4. Renderização de listas: .map()
(1) Uso básico
Array.map() Percorrer um array e retornar um JSX para cada elemento:
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>
(2) Matriz de objetos de renderização
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>
)
}
(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]
| 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
// ============================================
// 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
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
// ============================================
// 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
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>
)
}
▶ Exemplo 3: Pesquisar, filtrar e ordenar listas
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>
)
}
▶ Exemplo 4: Exibição de listas aninhadas — Exibição de produtos categorizados
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>
)
}
❓ 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: Quandoitems.lengthé 0, o resultado de0 && <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
indexcomo chave? **R: Há três problemas: ① Após ordenar ou filtrar uma lista, oindexpermanece 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 deindexmudam, exigindo que todo o DOM seja reconstruído; ③ Quando elementos são removidos, oindexavança, o que também pode causar inconsistências. Quando não houver IDs estáveis, use pelo menositem.text + Math.random()ouuuid()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 seriacondition ? <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.