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



2. Uma história real de uma equipe de front-end

Fluxo de dados entre componentes e props

100%
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:

JSX
// 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>
▶ Experimente

Pergunta:

(2) Uma abordagem baseada em componentes

Gerenciamento unificado por meio de componentes de função e props:

JSX
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>
▶ Experimente

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.

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>
▶ Experimente
Regra Descrição
Colocar a primeira letra em maiúscula Welcomewelcome ❌ (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.

JSX
// ---- 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>
▶ Experimente

(3) Sintaxe de desestruturação para props

Uma abordagem mais comum é a atribuição desconstrutiva, que recupera diretamente os parâmetros desejados:

JSX
// 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>
}
▶ Experimente

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

JSX
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" />
▶ Experimente

(2) Números, valores booleanos e objetos

Os tipos que não sejam de cadeia de caracteres devem ser colocados entre {}:

JSX
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 }}
/>
▶ Experimente

(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

JSX 📖 Somente leitura
// ============================================
// 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']}
/>
45 linhas de lógica (limite de 40, somente leitura)

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.

JSX
// 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>
▶ Experimente

(2) Valor padrão

Use a sintaxe de desestruturação do ES6 com valores padrão para definir valores padrão para as propriedades:

JSX
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"
▶ Experimente

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:

TSX
// ---- 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”

TSX
// ============================================
// 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

JSX 📖 Somente leitura
// ============================================
// 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 />
80 linhas de lógica (limite de 40, somente leitura)

▶ Exemplo 3: Props de callback de eventos — Componentes filhos notificando componentes pais

JSX
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>
  )
}
▶ Experimente

▶ Exemplo 4: Renderização condicional de props — Troca dinâmica de componentes

JSX
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>
  )
}
▶ Experimente

▶ Exemplo 5: O padrão renderProps — Componentes de lista e renderização personalizada

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

❓ 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' }) {}; ② Usar Card.defaultProps = { title: 'Untitled' }. Observe que o React não recomenda mais o uso de defaultProps (em componentes de função); recomenda-se usar a desestruturação para atribuir valores padrão. No TypeScript, você também pode usar title?: string para 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


📝 Exercícios

  1. Problema básico (Dificuldade ⭐): Crie um componente Avatar que aceite duas propriedades — name (uma string) e size (um número, com valor padrão 50) — e exiba o avatar e o nome do usuário.
  2. Problema avançado (Dificuldade ⭐⭐): Crie um componente TagList que aceite duas propriedades — tags (um array de strings) e onRemove (uma função) — e exiba uma lista de tags. Ao clicar em uma tag, ela é excluída.
  3. Desafio (Dificuldade ⭐⭐⭐): Crie um componente DataTable em TypeScript que aceite columns (um array de definições de colunas) e data (um array de dados) e exiba uma tabela de dados simples. Tente adicionar uma propriedade striped (booleano, opcional) para controlar o padrão zebra.
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%