Capítulo 02: Componentes Funcionais, Props, Tipagem Estrita e Composição
Especialização Frontend com React & TypeScript • React 19, Hooks, Estado Global e Next.js
🗺️ Mapa Conceitual do Tópico
flowchart TD
PAGE["Página Principal (DashboardPage)"]
PAGE -->|"Props (title, user)"| LAYOUT["ShellLayout"]
LAYOUT -->|"Children Composto"| SIDEBAR["NavigationSidebar"]
LAYOUT -->|"Children Composto"| CONTENT["MainContentArea"]
CONTENT -->|"Props Imutáveis"| METRIC["MetricCard"]
CONTENT -->|"Props Imutáveis"| TABLE["DataTable"]
style PAGE fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
style LAYOUT fill:#e1f5fe,stroke:#03a9f4,stroke-width:1.5px
style CONTENT fill:#fff3e0,stroke:#ff9800,stroke-width:1.5px
style METRIC fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px
style TABLE fill:#e8f5e9,stroke:#4caf50,stroke-width:1.5px
🏛️ 1. O Modelo de Componentes Funcionais e Props Imutáveis
No React moderno, Componentes Funcionais são a unidade básica de composição de software. Eles recebem um objeto de entrada denominado props e retornam uma árvore de elementos JSX.
Princípios da Tipagem de Props:
- Imutabilidade Estrita: As props são tratadas como somente leitura (read-only). Um componente filho nunca deve modificar suas próprias props (
props.title = "novo"é uma violação grave do modelo arquitetural). - Fluxo de Dados Unidirecional (One-Way Data Flow): Dados fluem de cima para baixo (pais para filhos) através de props. Eventos fluem de baixo para cima através de funções de callback (
onSelect,onChange).
🧩 2. Composição sobre Herança e o Padrão children
O React rejeita herança de classes em favor da Composição de Componentes. A propriedade especial children permite que componentes atuem como contêineres (Slots), recebendo qualquer conteúdo JSX arbitrário:
interface PanelProps {
title: string;
headerAction?: React.ReactNode;
children: React.ReactNode; // Aceita qualquer nó JSX válido
}
export function Panel({ title, headerAction, children }: PanelProps) {
return (
<section className="panel-container">
<header className="panel-header">
<h3>{title}</h3>
{headerAction}
</header>
<div className="panel-body">{children}</div>
</section>
);
}
📊 3. Comparativo de Padrões de Tipagem: React.FC vs Declaração Direta
| Aspecto de Tipagem | Padrão Histórico: React.FC<Props> |
Padrão Ouro Atual: Declaração Direta |
|---|---|---|
| Sintaxe | const Card: React.FC<Props> = ... |
function Card({ title, children }: Props) ... |
| Tipagem Implícita de Children | ⚠️ Em versões antigas incluía children mesmo sem querer |
✅ Tipagem explícita; children só existe se declarado na interface |
| Suporte a Componentes Genéricos | Complexo e verboso | ✅ Natural e intuitivo (function List<T>(...)) |
| Identificação em DevTools | Requer displayName manual em funções anônimas |
✅ Nome automático inferido da declaração da função |
⚠️ 4. Armadilhas Comuns e Anti-Padrões de Produção
- Prop Drilling Excessivo: Passar props através de 5 ou mais níveis de componentes intermediários que não utilizam o dado gera acoplamento espúrio. Resolva com Composição de Componentes (passando o componente pronto via
children) ou Context API. - Criação de Componentes Dentro de Componentes: Declarar uma função de componente dentro do corpo de outro componente faz com que o filho seja recriado em memória a cada render, perdendo seu estado interno e recriando o DOM do zero.
- Uso de Tipos Excessivamente Permissivos: Tipar props como
anyoudata: objectanula as garantias do TypeScript. Sempre utilize interfaces estritas com discriminated unions quando houver múltiplos modos de exibição.
💡 5. Diretrizes de Engenharia (Meta & React Docs)
- Desestruture Props Diretamente no Argumento: Forneça valores padrão diretamente na desestruturação (
function Button({ variant = "primary" }: ButtonProps)), tornando o contrato claro e auto-documentado. - Prefira Interfaces Específicas a Interfaces Genéricas: Evite estender
React.HTMLAttributes<HTMLDivElement>a menos que o componente seja explicitamente um wrapper genérico que necessite repassar todas as props nativas via spread.
💻 6. Demonstração Arquitetural do Conceito
// AlertBanner.tsx - Componente composto fortemente tipado
import React from "react";
export interface AlertBannerProps {
readonly variant: "info" | "success" | "warning" | "error";
readonly title: string;
readonly onClose?: () => void;
readonly children: React.ReactNode;
}
export function AlertBanner({ variant, title, onClose, children }: AlertBannerProps) {
return (
<aside className={`alert-box alert-${variant}`} role="alert">
<div className="alert-content">
<strong className="alert-title">{title}</strong>
<div className="alert-description">{children}</div>
</div>
{onClose && (
<button type="button" onClick={onClose} aria-label="Fechar alerta">
×
</button>
)}
</aside>
);
}
📋 7. Cheat-Sheet de Tipos Comuns no React com TypeScript
| Tipo do React | Finalidade de Uso | Exemplo de Aplicação |
|---|---|---|
React.ReactNode |
Qualquer nó renderizável pelo React (JSX, strings, números, portais, null). | Tipagem de children e slots flexíveis. |
React.ReactElement |
Exclusivamente um elemento JSX (objeto React válido). | Quando o componente exige um elemento concreto (não string). |
React.CSSProperties |
Objeto de estilos inline fortemente tipado com suporte a camelCase. | style={customStyles} seguro contra Liquid e erros de digitação. |
React.MouseEvent<HTMLButtonElement> |
Evento de clique nativo com tipagem do elemento alvo. | Callback onClick: (e: React.MouseEvent) => void. |
🔍 8. Diagnóstico de Performance & React DevTools Profiler
Ao inspecionar componentes compostos na árvore do DevTools:
- “Why did this render?”: Ative nas configurações do Profiler a opção “Record why each component rendered while profiling”. O DevTools apontará textualmente:
"props changed: [onRefresh, children]". - Estabilização de Children: Nós passados via
childrenpreservam sua identidade referencial entre renders do container pai, evitando re-renderizar desnecessariamente toda a subárvore interna.
🧪 9. Casos de Teste e Cenários de Borda Recomendados
- 🔍 Children Indefinido: Valide se o container renderiza graciosamente quando
childrenfornullouundefined. - 🔍 Props Parciais: Teste o comportamento do componente quando apenas as props mandatórias forem fornecidas, verificando os valores default.
- 🔍 Callbacks Não Fornecidos: Assegure que cliques em botões sem handler
onActionassociado não lancemTypeError.