React: Componentes de função e props
Última atualização: 2026-08-26
Os componentes de função são como peças de Lego — cada peça tem sua própria forma (props) e pode ser montada e combinada de várias maneiras. Os props são as “interfaces” das peças: seja qual for a cor ou o tamanho que você atribuir à peça, é assim que ela ficará.
1. O que você vai aprender
- Definição e convenções de nomenclatura para componentes de função
- Passar e receber adereços
- Tipos de propriedades (string / número / booleano / objeto)
- adereço especial para crianças
- Use o TypeScript para anotar os tipos das propriedades
2. Uma história real de uma equipe de front-end
Fluxo de dados entre componentes e props
flowchart TD
A[Parent Component] -->|"name='Alice'"| B[UserCard]
A -->|"items=[...]"| C[ProductList]
C -->|"item={...}"| D[ProductCard]
B -->|"children"| E[<Avatar />]
A -->|"onFollow callback"| B
style A fill:#e3f2fd,stroke:#1565c0
style B fill:#e8f5e9,stroke:#2e7d32
style C fill:#fff3e0,stroke:#e65100
style D fill:#fce4ec,stroke:#c62828
(1) Problema: Um botão que você precisa copiar e colar 50 vezes
Charlie é o líder técnico de uma equipe de front-end. Ele descobriu que havia mais de 50 botões diferentes espalhados por 20 páginas no repositório de código:
// Page A:Red Button
<button style={{ backgroundColor: 'red', color: 'white' }}>
Delete
</button>
// Page B:Blue Button
<button style={{ backgroundColor: 'blue', color: 'white' }}>
Submit
</button>
// Page C:Small gray button
<button style={{ backgroundColor: 'gray', color: 'black', fontSize: '12px' }}>
Cancel
</button>
Pergunta:
- Alterações no design: Todos os botões precisam ter cantos arredondados; isso exige alterações em 50 lugares.
- Inconsistência: Alguns botões usam
red, outros usam#ff0000e outros ainda usamrgb(255,0,0) - Erros frequentes: Alguém definiu o estilo incorretamente, fazendo com que o botão inteiro ficasse transparente
(2) Uma abordagem baseada em componentes
Gerenciamento unificado por meio de componentes de função e props:
function Button({ variant = 'primary', size = 'medium', children }) {
const styles = {
primary: { backgroundColor: '#1890ff', color: 'white' },
danger: { backgroundColor: '#ff4d4f', color: 'white' },
default: { backgroundColor: '#f0f0f0', color: '#333' }
}
return (
<button style={{
...styles[variant],
padding: size === 'large' ? '12px 24px' : '8px 16px',
border: 'none',
borderRadius: '6px',
cursor: 'pointer'
}}>
{children}
</button>
)
}
// Usage:Done in one line of code
<Button variant="danger">Delete</Button>
<Button variant="primary">Submit</Button>
<Button variant="default">Cancel</Button>
Benefícios: O número de botões foi reduzido de 50 fragmentos para 1 componente; agora, as alterações no design exigem apenas uma modificação, garantindo um estilo consistente.
3. Fundamentos dos componentes de função
(1) Definição de um componente
No React, um componente de função é uma função em JavaScript que retorna JSX.
// The Simplest Function Component
function Welcome() {
return <h1>Hello, React!</h1>
}
// How to Write Arrow Functions(More common)
const Welcome = () => {
return <h1>Hello, React!</h1>
}
// Implicit Return(Only one line JSX "when " can be omitted return)
const Welcome = () => <h1>Hello, React!</h1>
| Regra | Descrição |
|---|---|
| Colocar a primeira letra em maiúscula | Welcome ✅ welcome ❌ (O React distingue entre componentes e tags HTML) |
| Retornar JSX | Deve retornar um elemento JSX (ou null) |
| Funções puras | A mesma entrada (Props) deve retornar a mesma saída (UI) |
| Props não podem ser modificadas | As props são somente para leitura e não podem ser modificadas dentro do componente |
(2) Os props são argumentos de função
Props (abreviação de “Properties”) são dados somente para leitura passados de um componente pai para um componente filho.
// ---- Define a Component ----
function Greeting(props) {
// props It is an object,Includes all incoming parameters
return <h1>Hello,{props.name}!This year, you {props.age} years old。</h1>
}
// ---- Using Components ----
<Greeting name="Alice" age={28} />
// Rendering Results:
// <h1>Hello,Alice!This year, you 28 years old。</h1>
(3) Sintaxe de desestruturação para props
Uma abordagem mais comum é a atribuição desconstrutiva, que recupera diretamente os parâmetros desejados:
// Deconstructive Writing Style(Recommendations)
function Greeting({ name, age }) {
return <h1>Hello,{name}!This year, you {age} years old。</h1>
}
// equivalent to:
function Greeting(props) {
const { name, age } = props
return <h1>Hello,{name}!This year, you {age} years old。</h1>
}
4. Tipos de adereços
| Tipo de prop | Sintaxe de passagem | Exemplo de recepção | Observações |
|---|---|---|---|
| String | name="Alice" |
{ name } → “Alice” |
Entre aspas duplas |
| Número | age={30} |
{ age } → 30 |
É necessário usar {} |
| Booleano | active / active={true} |
{ active } → verdadeiro |
Se o valor for omitido, o padrão é verdadeiro |
| Objeto | info={{ key: 'val' }} |
{ info } → { key: 'val' } |
Chaves duplas |
| Matriz | items={[1, 2, 3]} |
{ items } → [1,2,3] |
É necessário usar {} |
| Função | onClick={handler} |
{ onClick } → fn |
Callback/Manipulador de eventos |
| Componente | icon={<Icon />} |
{ icon } → JSX |
Componente renderizado dinamicamente |
Os props podem aceitar qualquer tipo de dados em JavaScript: strings, números, valores booleanos, objetos, matrizes, funções e até mesmo outros componentes.
(1) String
As sequências de caracteres podem ser passadas diretamente usando aspas duplas (assim como os atributos HTML):
function UserCard({ name, role }) {
return (
<div className="card">
<h2>{name}</h2>
<p>Characters:{role}</p>
</div>
)
}
// Strings are enclosed in double quotes,For other types {} Package
<UserCard name="Alice" role="Administrator" />
(2) Números, valores booleanos e objetos
Os tipos que não sejam de cadeia de caracteres devem ser colocados entre {}:
function Product({ name, price, inStock, details }) {
return (
<div className="product">
<h3>{name}</h3>
<p>Price:${price}</p>
{/* Boolean values are used for conditional rendering */}
{inStock ? <span>In stock ✅</span> : <span>Out of stock ❌</span>}
{/* Object:Accessible properties */}
<p>Category:{details.category} | Weight:{details.weight}g</p>
</div>
)
}
// Using Components
<Product
name="Wireless Mouse"
price={89}
inStock={true}
details={{ category: 'Peripherals', weight: 120 }}
/>
(3) Tabela de referência rápida para métodos de passagem por valor
| Tipo | Exemplo | Descrição |
|---|---|---|
| String | name="Alice" |
Escrito da mesma forma que os atributos HTML |
| Número | count={42} |
Requer {} |
| Valor booleano true | active ou active={true} |
Se apenas o nome da propriedade for especificado, o valor padrão é true |
| Valor booleano falso | active={false} |
Requer {} |
| Objeto | data={{ x:1, y:2 }} |
Chaves duplas |
| Matriz | items={[1,2,3]} |
Requer {} |
| Expressão | result={a + b} |
Qualquer expressão em JS |
| Variável | name={userName} |
Variável passada por valor |
▶ Exemplo: Cartão de informações do usuário
// ============================================
// Example:use Props Build a User Information Card
// Features:Display Props Transmission of Various Types
// ============================================
function UserCard({ name, age, isOnline, avatar, tags }) {
return (
<div style={{
border: '1px solid #ddd',
borderRadius: '8px',
padding: '20px',
maxWidth: '300px'
}}>
<img
src={avatar}
alt={name}
style={{ width: '80px', height: '80px', borderRadius: '50%' }}
/>
<h2>{name}</h2>
<p>Age:{age} years old</p>
<p>
Status:
<span style={{ color: isOnline ? '#52c41a' : '#999' }}>
{isOnline ? 'Online' : 'Offline'}
</span>
</p>
<div>
Tags:
{tags.map(tag => (
<span key={tag} style={{
background: '#f0f0f0',
padding: '2px 8px',
borderRadius: '4px',
margin: '0 4px',
fontSize: '12px'
}}>
{tag}
</span>
))}
</div>
</div>
)
}
// Using Components
<UserCard
name="Alice"
age={28}
isOnline={true}
avatar="https://i.pravatar.cc/80"
tags={['Front End', 'React', 'TypeScript']}
/>
5. Elementos filhos e valores padrão
(1) filhos: conteúdo aninhado dentro do componente
children é um Prop especial que representa o conteúdo entre as tags de componente.
// Definition:use children Content between tags
function Card({ children, title }) {
return (
<div style={{ border: '1px solid #ddd', padding: '16px', borderRadius: '8px' }}>
<h3>{title}</h3>
<div>{children}</div> {/* The content between the tags is displayed here */}
</div>
)
}
// Usage:The content between the tags automatically becomes children
<Card title="Announcement">
<p>The system will go live tonight 22:00 Perform maintenance,Expected to continue 2 hours。</p>
<p>Please save your work in advance.,To prevent data loss。</p>
</Card>
// Rendering Results:
// <div class="card">
// <h3>Announcement</h3>
// <div>
// <p>The system will go live tonight 22:00 Perform maintenance...</p>
// <p>Please save your work in advance....</p>
// </div>
// </div>
(2) Valor padrão
Use a sintaxe de desestruturação do ES6 com valores padrão para definir valores padrão para as propriedades:
function Button({ text = 'Click', color = 'blue', size = 'medium' }) {
return (
<button style={{
backgroundColor: color,
padding: size === 'large' ? '12px 24px' : '8px 16px',
color: 'white',
border: 'none',
borderRadius: '4px'
}}>
{text}
</button>
)
}
// No parameters → Use the default values
<Button /> // Blue、medium、"Click"
// Pass some parameters → Override the default value
<Button text="Submit" /> // Blue、medium、"Submit"
<Button color="red" size="large" /> // Red、large、"Click"
6. Anotações de tipo no TypeScript (Visão geral)
Usando o TypeScript, é possível definir tipos rigorosos para as props, permitindo que o editor ofereça sugestões de autocompletar e realize verificações de tipo:
// ---- Definition Props Type ----
interface ButtonProps {
text: string // Required:string
color?: string // Optional: add ? suffix
size?: 'small' | 'medium' | 'large' // Optional: limited to 3 values
onClick: () => void // Required:Function Types
disabled?: boolean // Optional: boolean, defaults to false
}
// ---- Using in a component ----
function Button({ text, color = 'blue', size = 'medium', onClick, disabled = false }: ButtonProps) {
return (
<button
onClick={onClick}
disabled={disabled}
style={{
backgroundColor: color,
padding: size === 'large' ? '12px 24px' : '8px 16px',
opacity: disabled ? 0.5 : 1,
color: 'white',
border: 'none',
borderRadius: '4px'
}}
>
{text}
</button>
)
}
// Usage:The editor provides smart suggestions
<Button
text="Save"
color="#1890ff"
size="medium"
onClick={() => alert('Saved successfully!')}
/>
// ❌ Error:TypeScript It will throw an error
<Button text="Test" /> // Required field is missing onClick
<Button text="Test" size="xl" onClick={fn} /> // size Outside the permitted range
7. Exemplo completo: Componente “Ficha do produto”
// ============================================
// Example:Product Card Component(Comprehensive Application Props Various Types)
// ============================================
// Type Definitions
interface Product {
id: number
name: string
price: number
image: string
category: string
tags: string[]
}
interface ProductCardProps {
product: Product
onAddToCart: (productId: number) => void
showDiscount?: boolean
size?: 'small' | 'large'
}
// Component Implementation
function ProductCard({
product,
onAddToCart,
showDiscount = false,
size = 'large'
}: ProductCardProps) {
return (
<div style={{
border: '1px solid #eee',
borderRadius: '8px',
padding: size === 'large' ? '16px' : '10px',
width: size === 'large' ? '280px' : '200px'
}}>
<img
src={product.image}
alt={product.name}
style={{ width: '100%', height: '150px', objectFit: 'cover' }}
/>
<h3>{product.name}</h3>
<p style={{ color: '#ff4d4f', fontSize: '20px', fontWeight: 'bold' }}>
${product.price}
{showDiscount && <span style={{ fontSize: '12px', color: '#999', textDecoration: 'line-through', marginLeft: '8px' }}>${(product.price * 1.2).toFixed(0)}</span>}
</p>
<p style={{ color: '#666', fontSize: '14px' }}>{product.category}</p>
<div>
{product.tags.map(tag => (
<span key={tag} style={{ background: '#f0f5ff', color: '#1890ff', padding: '2px 6px', borderRadius: '4px', margin: '0 2px', fontSize: '12px' }}>
{tag}
</span>
))}
</div>
<button
onClick={() => onAddToCart(product.id)}
style={{
width: '100%',
padding: '8px',
marginTop: '10px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer'
}}
>
Add to Cart
</button>
</div>
)
}
// Using Components
const myProduct = {
id: 101,
name: 'Mechanical Keyboard',
price: 299,
image: 'https://via.placeholder.com/280x150',
category: 'Computer Peripherals',
tags: ['Hot Seller', 'New Products']
}
<ProductCard
product={myProduct}
onAddToCart={(id) => console.log('Add to Cart:', id)}
showDiscount={true}
/>
Resultado esperado: Uma ficha de produto exibindo a imagem de um teclado, o nome “Teclado Mecânico”, o preço de US$ 299 (com o preço original de US$ 359 riscado), a categoria “Periféricos de computador” e as tags “Mais vendido” e “Novidade”, com um botão azul “Adicionar ao carrinho” abaixo.
▶ Exemplo 2: Uso abrangente da passagem de tipos de props e valores padrão
// ============================================
// Example:Profile Card——Props Combined Use of Types and Default Values
// Features:Display string、number、boolean、object、function etc. Props Type Passing
// ============================================
function ProfileCard({
name = 'Anonymous User',
age = 0,
isActive = false,
avatar = 'https://via.placeholder.com/60',
skills = [],
onGreet
}) {
return (
<div style={{
border: '1px solid #e8e8e8',
borderRadius: '12px',
padding: '20px',
maxWidth: '320px',
boxShadow: '0 2px 8px rgba(0,0,0,0.06)'
}}>
<div style={{ display: 'flex', alignItems: 'center', gap: '16px', marginBottom: '16px' }}>
<img
src={avatar}
alt={name}
style={{ width: '60px', height: '60px', borderRadius: '50%', objectFit: 'cover' }}
/>
<div>
<h3 style={{ margin: 0 }}>{name}</h3>
<p style={{ margin: '4px 0 0 0', color: '#666', fontSize: '14px' }}>
{age} years old · {isActive ? '🟢 Online' : '🔴 Offline'}
</p>
</div>
</div>
{skills.length > 0 && (
<div style={{ marginBottom: '12px' }}>
<p style={{ margin: '0 0 8px 0', fontWeight: 'bold', fontSize: '14px' }}>Skills:</p>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '6px' }}>
{skills.map((skill, index) => (
<span key={index} style={{
backgroundColor: '#f0f5ff',
color: '#1890ff',
padding: '4px 10px',
borderRadius: '12px',
fontSize: '12px'
}}>
{skill}
</span>
))}
</div>
</div>
)}
{onGreet && (
<button
onClick={() => onGreet(name)}
style={{
width: '100%',
padding: '8px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '6px',
cursor: 'pointer',
fontSize: '14px'
}}
>
Say hello to {name}
</button>
)}
</div>
)
}
// Usage:Pass all parameters
<ProfileCard
name="Alice"
age={28}
isActive={true}
avatar="https://i.pravatar.cc/60"
skills={['React', 'TypeScript', 'Node.js']}
onGreet={(name) => alert(`Hello,${name}!`)}
/>
// Usage:Pass only some parameters(Use the default values for the rest)
<ProfileCard
name="Bob"
skills={['Vue', 'Python']}
/>
// Usage:Do not pass any parameters(Use all default values)
<ProfileCard />
▶ Exemplo 3: Props de callback de eventos — Componentes filhos notificando componentes pais
function AddToCartButton({ productName, onAdd }) {
const [adding, setAdding] = useState(false)
async function handleClick() {
setAdding(true)
await onAdd(productName)
setAdding(false)
}
return (
<button onClick={handleClick} disabled={adding}
style={{ padding: '8px 16px', cursor: adding ? 'wait' : 'pointer' }}>
{adding ? 'Adding...' : `Add ${productName}`}
</button>
)
}
function ProductPage() {
const [cart, setCart] = useState([])
function handleAdd(name) {
setCart(prev => [...prev, name])
}
return (
<div>
<h2>Products</h2>
<AddToCartButton productName="Keyboard" onAdd={handleAdd} />
<AddToCartButton productName="Mouse" onAdd={handleAdd} />
<p>Cart: {cart.join(', ') || 'empty'}</p>
</div>
)
}
▶ Exemplo 4: Renderização condicional de props — Troca dinâmica de componentes
function StatusMessage({ status }) {
const config = {
loading: { icon: '⏳', color: '#1890ff', text: 'Loading...' },
success: { icon: '✅', color: '#52c41a', text: 'Success!' },
error: { icon: '❌', color: '#ff4d4f', text: 'Error occurred' },
empty: { icon: '📭', color: '#999', text: 'No data' },
}
const { icon, color, text } = config[status] || config.empty
return (
<div style={{ padding: 16, textAlign: 'center', color }}>
<span style={{ fontSize: 32 }}>{icon}</span>
<p>{text}</p>
</div>
)
}
function DataViewer() {
const [status, setStatus] = useState('loading')
return (
<div style={{ maxWidth: 300, margin: '0 auto' }}>
<StatusMessage status={status} />
<div style={{ display: 'flex', gap: 4 }}>
{['loading', 'success', 'error', 'empty'].map(s => (
<button key={s} onClick={() => setStatus(s)}
style={{ padding: '4px 8px', cursor: 'pointer', fontSize: 12 }}>
{s}
</button>
))}
</div>
</div>
)
}
▶ Exemplo 5: O padrão renderProps — Componentes de lista e renderização personalizada
function DataList({ items, renderItem, keyExtractor, emptyMessage = 'No items' }) {
if (!items || items.length === 0) {
return <p style={{ color: '#999', textAlign: 'center', padding: 20 }}>{emptyMessage}</p>
}
return (
<ul style={{ listStyle: 'none', padding: 0 }}>
{items.map(item => (
<li key={keyExtractor(item)} style={{ borderBottom: '1px solid #f0f0f0', padding: 8 }}>
{renderItem(item)}
</li>
))}
</ul>
)
}
function App() {
const users = [
{ id: 1, name: 'Alice', email: 'alice@example.com', active: true },
{ id: 2, name: 'Bob', email: 'bob@example.com', active: false },
{ id: 3, name: 'Charlie', email: 'charlie@example.com', active: true },
]
const products = [
{ id: 'a', name: 'Keyboard', price: 79 },
{ id: 'b', name: 'Monitor', price: 399 },
]
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<h3>Users</h3>
<DataList
items={users}
keyExtractor={u => u.id}
renderItem={u => (
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
<span>{u.name} ({u.email})</span>
<span style={{ color: u.active ? '#52c41a' : '#999' }}>
{u.active ? 'Active' : 'Inactive'}
</span>
</div>
)}
/>
<h3>Products</h3>
<DataList
items={products}
keyExtractor={p => p.id}
emptyMessage="No products available"
renderItem={p => (
<span>{p.name} - ${p.price}</span>
)}
/>
</div>
)
}
❓ Perguntas Frequentes
P: Por que os props são somente para leitura? Eles não podem ser modificados diretamente? R: A filosofia de design do React é o “fluxo unidirecional de dados” — os dados fluem dos componentes pais para os componentes filhos, e os componentes filhos não podem modificar os dados do pai. Se um componente filho precisar modificar dados, ele deve notificar o componente pai para que isso seja feito por meio de uma função de retorno de chamada passada pelas props (para manter uma “única fonte de verdade”). Isso garante um fluxo de dados claro, facilitando a depuração e a manutenção.
P: Qual é a diferença entre props e state? R: Os props são passados de fora e são somente de leitura; o state é gerenciado internamente pelo componente e é mutável. Você pode pensar nos props como os “parâmetros de configuração” do componente e no state como seu “estado interno”. Se os props mudarem, o componente é renderizado novamente; se o state mudar, o componente é renderizado novamente.
P: Os nomes dos componentes precisam estar em maiúsculas? O que acontece se estiverem em minúsculas? R: Eles devem estar em maiúsculas. O React distingue entre componentes e tags HTML com base no uso de maiúsculas:
<div>é uma tag div do HTML;<Div>é um componente personalizado. Se um componente for escrito em minúsculas, o React o tratará como uma tag HTML, fazendo com que a renderização falhe.
P: Até que profundidade os componentes podem ser aninhados? Existem limites? R: Não há limites técnicos, mas recomendamos não ultrapassar 3 a 4 níveis. Um aninhamento excessivamente profundo pode complicar a passagem de dados (prop drilling) e reduzir a legibilidade do código. Se você precisar passar dados por vários níveis, considere usar a API Context (Lição 13) ou o gerenciamento de estado global (Lição 18).
P: Como defino valores padrão para props? R: Existem duas maneiras: ① Atribuir valores padrão durante a desestruturação
function Card({ title = 'Untitled' }) {}; ② UsarCard.defaultProps = { title: 'Untitled' }. Observe que o React não recomenda mais o uso dedefaultProps(em componentes de função); recomenda-se usar a desestruturação para atribuir valores padrão. No TypeScript, você também pode usartitle?: stringpara indicar que uma propriedade é opcional, o que torna o código mais claro quando combinado com a desestruturação para atribuir valores padrão.
📖 Resumo
- Um componente de função é uma função JavaScript que retorna JSX; a primeira letra deve estar em maiúscula
- Props são dados somente para leitura passados de um componente pai para um componente filho; o componente filho não pode modificá-los.
- Os parâmetros podem ser de qualquer tipo: string, número, booleano, objeto, array ou função
childrené um Prop especial que representa o conteúdo entre as tags de componente- Use a sintaxe de desestruturação
function Comp({ prop1, prop2 })para tornar seu código mais claro - As anotações de tipo do TypeScript podem melhorar significativamente a experiência de desenvolvimento e a robustez do código
📝 Exercícios
- Problema básico (Dificuldade ⭐): Crie um componente
Avatarque aceite duas propriedades —name(uma string) esize(um número, com valor padrão 50) — e exiba o avatar e o nome do usuário. - Problema avançado (Dificuldade ⭐⭐): Crie um componente
TagListque aceite duas propriedades —tags(um array de strings) eonRemove(uma função) — e exiba uma lista de tags. Ao clicar em uma tag, ela é excluída. - Desafio (Dificuldade ⭐⭐⭐): Crie um componente
DataTableem TypeScript que aceitecolumns(um array de definições de colunas) edata(um array de dados) e exiba uma tabela de dados simples. Tente adicionar uma propriedadestriped(booleano, opcional) para controlar o padrão zebra.