Gerenciamento de Estado Avançado: Zustand, Jotai e Recoil Comparados na Prática Já leu

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 para definir sua store: O grande diferencial é a ausência de boilerplate. Você não precisa de reducers, actions ou

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.

Referências


Artigos relacionados