Quando o estado do componente é suficiente em telas mobile. Use useReducer quando múltiplos campos são atualizados juntos - passos de checkout, wizards de onboarding e formulários com muita validação. Mantenha respostas de API no TanStack Query; reducers gerenciam apenas transições de UI do cliente.
O dispatch de useReducer é estável durante o tempo de vida do componente - omita-o dos arrays de dependência do useEffect. O mesmo vale para setState do useState. O documento regras de lint do React Hooks detalha isso.
Mantenha wizardReducer no mesmo arquivo que OnboardingWizard até que uma segunda tela precise de transições idênticas. Promova para features/onboarding/wizardReducer.ts apenas em caso de reutilização - não preventivamente.
Muitas chamadas useState não relacionadas - Cinco booleanos para um formulário significam atualizações perdidas. Correção: Um useReducer ou React Hook Form.
Mutação de estado dentro do reducer - state.items.push(x) quebra o React. Correção: Retorne novos objetos: { ...state, items: [...state.items, x] }.
Efeitos colaterais no reducer - Buscar dados dentro do switch dificulta os testes. Correção: Efeitos colaterais em manipuladores de eventos; o reducer permanece puro.
Armazenar listas de API no reducer - Duplica o cache do TanStack Query. Correção: Use Query para dados do servidor; reducer para rascunho apenas de UI.
Elevar o reducer demais - O estado do wizard no Contexto raiz re-renderiza o aplicativo. Correção: Mantenha o reducer na tela do wizard até que outra rota realmente precise dele.
Caso default ausente - A exaustividade do TypeScript ajuda; ações desconhecidas em tempo de execução devem retornar state.
Atualizações funcionais com reducer - Reducers já recebem o estado anterior - não aninhe padrões de setState dentro deles.
useState ou useReducer para um formulário de login?
Dois campos (e-mail, senha) - useState é suficiente. Se você adicionar "lembrar-me", um passo de MFA e validação inline, mude para useReducer ou Hook Form.
Posso usar useReducer com Context?
Sim - useReducer em um provider é o padrão global integrado. Divida contextos ou use Zustand quando os consumidores re-renderizarem com muita frequência - veja Context Without Storms.
useReducer substitui o Redux?
Apenas para estado de UI local ou pequeno e compartilhado. Redux Toolkit ainda vence para domínios multi-funcionalidade auditados e DevTools em escala - veja Redux Toolkit.
Como testar um reducer?
Exporte wizardReducer e teste ações unitariamente sem renderizar:
expect(wizardReducer(initial, { type: "next" })).toEqual({ ...initial, error: "O nome deve ter pelo menos 2 caracteres" });
O envio (submitting) deve estar no reducer?
Mantenha submitting como useState ou derive de useMutationisPending. O status assíncrono é um efeito colateral, não uma transição pura.
E sobre o useActionState do React 19?
useActionState visa Server Actions na web. Aplicativos mobile Expo usam useReducer + useMutation do TanStack Query para envios de rede.
Quantos hooks useState são demais?
Regra geral: mais de quatro valores relacionados em uma tela → considere um reducer ou um hook customizado que envolva o estado.
Posso usar immer com useReducer?
Sim, via useImmerReducer do use-immer. Zustand + middleware immer é mais comum para stores globais - veja Zustand.
Como wizards interagem com o Expo Router?
Mantenha o índice do passo no estado do reducer para transições animadas. Para progresso compartilhável, espelhe o passo em router.setParams({ step: "2" }) - a URL é uma UX opcional, o reducer é a fonte da verdade durante a sessão.
Por que não colocar tudo em um grande objeto useState?
setState({ ...s, email }) funciona até que duas atualizações sejam agrupadas incorretamente ou você esqueça um campo. Reducers forçam nomes de ação explícitos - mais fácil de registrar e testar.
Como isso se relaciona com container/presenter?
O container mantém useReducer / useState; o presenter recebe apenas props e callbacks - veja container-presenter on mobile.
Performance em listas longas?
O estado do reducer em um componente pai de uma FlatList com 500 linhas re-renderiza todas as linhas. Mova os dados da lista para Query; mantenha a seleção no reducer com extraData ou memoização no nível da linha.