Introdução ao Gerenciamento de Estado Moderno
O gerenciamento de estado é um dos desafios centrais em aplicações React modernas. Ao longo dos anos, passamos de Redux—com toda sua verbosidade—para soluções mais minimalistas e intuitivas. Zustand, Jotai e Recoil representam essa evolução, cada uma com filosofias distintas. Neste artigo, exploraremos essas três bibliotecas, seus trade-offs e quando usá-las, para que você possa tomar decisões informadas em seus projetos.
Zustand: Simplicidade e Minimalismo
Zustand é uma biblioteca extremamente leve (2.9KB) que oferece uma API simples e direta. Sua filosofia é: "você não precisa de tanto código para gerenciar estado". A biblioteca usa Hooks do React nativamente e funciona sem providers, tornando-a ideal para projetos que valorizam simplicidade.
Características principais
Zustand armazena estado em uma store única (embora você possa criar múltiplas) e oferece seletores granulares para evitar re-renders desnecessários. O padrão é usar create para definir sua store:
import create from 'zustand';
const useCounterStore = create((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
reset: () => set({ count: 0 }),
}));
// Uso em componente
function Counter() {
const count = useCounterStore((state) => state.count);
const increment = useCounterStore((state) => state.increment);
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>+1</button>
</div>
);
}
O grande diferencial é a ausência de boilerplate. Você não precisa de reducers, actions ou dispatches—apenas uma função que retorna seu estado e seus métodos. Para estado mais complexo, Zustand ainda mantém a elegância:
const useAppStore = create((set) => ({
user: null,
todos: [],
setUser: (user) => set({ user }),
addTodo: (todo) => set((state) => ({
todos: [...state.todos, todo],
})),
loading: false,
setLoading: (bool) => set({ loading: bool }),
}));
Jotai: Abordagem Atômica e Composável
Jotai (que significa "estado" em japonês) traz uma perspectiva diferente: em vez de uma store centralizada, você trabalha com átomos—unidades isoladas de estado. Isso permite composição granular e é especialmente poderoso em aplicações complexas onde diferentes partes precisam de estados independentes mas relacionados.
Arquitetura e uso
Jotai separa lógica de estado da lógica de componentes através de átomos primitivos:
import { atom, useAtom, useAtomValue, useSetAtom } from 'jotai';
const countAtom = atom(0);
const nameAtom = atom('John');
// Átomos derivados (computed atoms)
const greetingAtom = atom((get) => {
const count = get(countAtom);
const name = get(nameAtom);
return `Hello ${name}, you have ${count} items`;
});
function Component() {
const [count, setCount] = useAtom(countAtom);
const greeting = useAtomValue(greetingAtom);
const setName = useSetAtom(nameAtom);
return (
<div>
<p>{greeting}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setName('Alice')}>Change Name</button>
</div>
);
}
A vantagem aqui é a reatividade natural: átomos derivados atualizam automaticamente quando suas dependências mudam. Jotai também suporta async atoms nativamente, ideal para requisições de API:
const userAtom = atom(
async (get) => {
const response = await fetch('/api/user');
return response.json();
}
);
function UserProfile() {
const user = useAtomValue(userAtom);
if (!user) return <p>Loading...</p>;
return <p>User: {user.name}</p>;
}
Recoil: Reatividade Profunda e DevTools
Recoil, desenvolvido pelo Facebook, é a solução mais sofisticada das três. Oferece um sistema reativo completo com selectors, efeitos colaterais (effects) e ferramentas avançadas de debug. É ideal quando você precisa de estado altamente reativo com dependências complexas.
Estrutura e padrões
Recoil funciona com átomos e selectors, mas adiciona camadas de funcionalidade como efeitos e validação:
import { atom, selector, useRecoilState, useRecoilValue } from 'recoil';
const userIdAtom = atom({
key: 'userId',
default: null,
});
const userSelector = selector({
key: 'user',
get: async ({ get }) => {
const userId = get(userIdAtom);
if (!userId) return null;
const response = await fetch(`/api/users/${userId}`);
return response.json();
},
});
function UserDetail() {
const userId = useRecoilState(userIdAtom);
const user = useRecoilValue(userSelector);
return <div>{user?.name}</div>;
}
Recoil também permite effects para sincronização com localStorage ou APIs:
const persistedCountAtom = atom({
key: 'persistedCount',
default: 0,
effects: [
({ setSelf, onSet }) => {
// Carregar do localStorage ao inicializar
setSelf(localStorage.getItem('count') ?? 0);
// Salvar quando mudar
onSet((newValue) => {
localStorage.setItem('count', newValue);
});
},
],
});
Comparação Prática
Zustand vence em simplicidade e tamanho. Use quando você quer estado gerenciado sem complexidade.
Jotai é superior para composição e múltiplas unidades de estado independentes. Melhor para microfrontends.
Recoil é mais poderoso para dependências complexas e async, com melhor DevTools.
Uma tabela mental: Zustand = Redux simplificado. Jotai = estado atômico. Recoil = reatividade total.
Conclusão
Aprendemos que não existe "melhor" biblioteca—existe a mais adequada para seu contexto. Zustand é sua escolha se simplicidade é prioridade e seu estado é relativamente plano. Jotai brilha em aplicações modernas com muitos pedaços de estado independentes e composição. Recoil é a escolha para aplicações complexas que exigem reatividade profunda e sincronização elegante.
Minha recomendação profissional: comece com Zustand para 80% dos projetos. Use Jotai se trabalha com múltiplos átomos independentes. Reserve Recoil para when you need advanced reactivity patterns and are willing to accept a larger bundle and slightly steeper learning curve.