Especialização Frontend com React & TypeScript • React 19, Hooks, Estado Global e Next.js


🗺️ Mapa Conceitual do Tópico

flowchart LR
    ACTION["Action { type: 'ADD', payload }"] --> DISPATCH["dispatch(action)"]
    DISPATCH --> REDUCER["Reducer Puro (state, action) => newState"]
    REDUCER --> NEW_STATE["Novo Estado Imutável Consolidado"]
    NEW_STATE --> UI["Interface Re-renderizada"]

🏛️ 1. O Hook useReducer: Máquinas de Estado Previsíveis

Quando a lógica de estado de um componente envolve múltiplos sub-valores ou quando o próximo estado depende de transições condicionais complexas, o useReducer é arquiteturalmente superior ao useState.

Inspirado na arquitetura Redux / Elm, ele desacopla a intenção da ação (dispatch({ type: 'INCREMENT' })) da execução da mutação (o Redutor puro).


🛡️ 2. Redutores como Funções Puras

Um Redutor deve ser uma função pura:

  • Não dispara efeitos colaterais (zero requisições HTTP, zero mutações de variáveis externas).
  • Produz o mesmo estado de saída para o mesmo estado de entrada e a mesma ação.
  • Retorna sempre uma nova referência de estado com imutabilidade estrita.

📊 3. Comparativo: useState vs useReducer

Critério de Escolha useState useReducer
Complexidade de Estado Estados primitivos ou objetos simples Estados relacionais com muitas propriedades
Lógica de Transição Lógica dispersa pelos handlers de eventos Lógica centralizada em uma única função pura
Facilidade de Teste Unitário Exige renderizar o componente O redutor pode ser testado isoladamente sem DOM!
Tipagem TypeScript Simples Altamente expressiva com Discriminated Unions

⚠️ 4. Armadilhas Comuns e Anti-Padrões de Produção

  1. Efeitos Colaterais Dentro do Redutor: Disparar fetch(), localStorage.setItem() ou alterar variáveis globais dentro do redutor quebra o determinismo do React e impede o funcionamento do StrictMode.
  2. Mutação de Estado no Redutor: Fazer state.itens.push(action.payload) dentro de um case causa bugs de reatividade idênticos ao useState. Use spread ou bibliotecas como Immer.

💡 5. Diretrizes de Engenharia (Meta & React Docs)

  • Modele Actions com Discriminated Unions: Defina a propriedade type como literais de string estritos ("ADD" | "DELETE"), permitindo que o TypeScript refine automaticamente o formato de payload em cada case.
  • Utilize a Cláusula Default Segura: No switch, retorne o próprio estado inalterado ou lance um erro explicativo para ações não tratadas.

💻 6. Demonstração Arquitetural do Conceito

// authReducer.ts - Redutor puro fortemente tipado
export interface AuthState {
  readonly isAuthenticated: boolean;
  readonly user: string | null;
}

export type AuthAction =
  | { type: "LOGIN"; payload: { username: string } }
  | { type: "LOGOUT" };

export function authReducer(state: AuthState, action: AuthAction): AuthState {
  switch (action.type) {
    case "LOGIN":
      return { isAuthenticated: true, user: action.payload.username };
    case "LOGOUT":
      return { isAuthenticated: false, user: null };
    default:
      return state;
  }
}

📋 7. Cheat-Sheet da Arquitetura do Redutor

  1. State: Representação de dados em memória.
  2. Action: Mensagem declarativa com type e payload.
  3. Dispatch: Função despachante exposta pelo React.
  4. Reducer: Função pura que calcula f(state, action) -> newState.

🔍 8. Diagnóstico de Performance & React DevTools Profiler

Ao auditar fluxos com useReducer:

  • Rastreabilidade de Ações: O DevTools exibe cada ação despachada com seu respectivo type e payload.
  • Pureza do Redutor: O StrictMode executa o redutor duas vezes consecutivas em ambiente local; se os estados resultantes divergirem, o redutor possui efeitos colaterais impuros proibidos.

🧪 9. Casos de Teste e Cenários de Borda Recomendados

  • 🔍 Ação Desconhecida: Verifique se o redutor preserva o estado atual sem mutação ao receber uma ação não mapeada.
  • 🔍 Carrinho Vazio com Cupons: Teste o cálculo de descontos percentuais quando o subtotal do carrinho for zero.
  • 🔍 Operações Concorrentes: Simule despachos rápidos de ADD_ITEM e REMOVE_ITEM em sequência.