useReducer: estado complexo com ações explícitas
1 min de leitura
Quando um estado tem várias operações relacionadas, espalhar muitos setters pode
esconder as regras de mudança. useReducer concentra essas transições em uma
função pura que recebe state e action.
import { useReducer } from 'react';
function cartReducer(state, action) {
switch (action.type) {
case 'added':
return [...state, action.item];
case 'removed':
return state.filter((item) => item.id !== action.id);
case 'cleared':
return [];
default:
throw new Error(`Ação desconhecida: ${action.type}`);
}
}
export default function Cart() {
const [items, dispatch] = useReducer(cartReducer, []);
function addCoffee() {
dispatch({
type: 'added',
item: { id: crypto.randomUUID(), name: 'Catuaí' },
});
}
return (
<section>
<p>{items.length} item(ns)</p>
<button onClick={addCoffee}>Adicionar café</button>
<button onClick={() => dispatch({ type: 'cleared' })}>Limpar</button>
<ul>
{items.map((item) => (
<li key={item.id}>
{item.name}
<button onClick={() => dispatch({ type: 'removed', id: item.id })}>
Remover
</button>
</li>
))}
</ul>
</section>
);
}
As ações descrevem o que aconteceu; o reducer decide como o estado muda. Isso facilita testar regras e acompanhar transições quando o domínio cresce.
Mini missão: painel de missões
Troque o carrinho por uma lista de missões com estados pending, active e
done. Modele ações como started, completed, reopened e cleared.
Quando os nomes das ações parecem uma história do domínio, o reducer fica mais
fácil de ler.
- Reducer: transforma estado atual + ação em próximo estado.
- Action: é um objeto que descreve um evento relevante.
dispatch: envia ações para o reducer.
Dica: não migre um contador simples para reducer só porque ele existe. Use quando várias transições relacionadas ficam difíceis de acompanhar com setters.
No próximo nó, vamos usar todos esses conceitos para decidir onde cada estado e cada responsabilidade devem morar.
// Quiz
Qual é a responsabilidade de um reducer?