Capítulo 07: Estados Complexos com useReducer e Redutores Puros
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
- 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. - Mutação de Estado no Redutor: Fazer
state.itens.push(action.payload)dentro de umcasecausa bugs de reatividade idênticos aouseState. Use spread ou bibliotecas como Immer.
💡 5. Diretrizes de Engenharia (Meta & React Docs)
- Modele Actions com Discriminated Unions: Defina a propriedade
typecomo literais de string estritos ("ADD" | "DELETE"), permitindo que o TypeScript refine automaticamente o formato depayloadem cadacase. - 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
- State: Representação de dados em memória.
- Action: Mensagem declarativa com
typeepayload. - Dispatch: Função despachante exposta pelo React.
- 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
typeepayload. - 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_ITEMeREMOVE_ITEMem sequência.