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

  1. 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.
  2. 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.
  3. Uso de Tipos Excessivamente Permissivos: Tipar props como any ou data: object anula 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 children preservam 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 children for null ou undefined.
  • 🔍 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 onAction associado não lancem TypeError.