Zustand e React Query: A Dupla Dinâmica para o Estado no React
Zustand e o React Query, que, quando utilizados em conjunto, oferecem uma abordagem eficiente e simplificada para o gerenciamento de estado.
O que é o Zustand?
Zustand é uma biblioteca de gerenciamento de estado para React que se destaca pela sua simplicidade e leveza. Com uma API intuitiva, permite a criação de stores globais sem a necessidade de configurações complexas ou boilerplate excessivo.
Principais Características:
- Leve e Rápido: Com apenas alguns kilobytes, o Zustand não adiciona peso significativo à aplicação.
- Sem Boilerplate: Diferentemente de outras soluções, não requer a criação de reducers ou actions, tornando o código mais limpo e direto.
- API Simples: Através do hook
useStore
, é possível acessar e manipular o estado de forma direta e eficiente. - Suporte a Ações Assíncronas: Permite a integração direta com operações assíncronas, facilitando o gerenciamento de efeitos colaterais.
Exemplo Simples de Zustand
Aqui está um exemplo básico de como usar Zustand para gerenciar um contador global:
import { create } from 'zustand'; // Criando a store const useCounterStore = create((set) => ({ count: 0, increase: () => set((state) => ({ count: state.count + 1 })), decrease: () => set((state) => ({ count: state.count - 1 })), })); function Counter() { const { count, increase, decrease } = useCounterStore(); return ( <div> <h2>Contador: {count}</h2> <button onClick={increase}>Aumentar</button> <button onClick={decrease}>Diminuir</button> </div> ); } export default Counter;
Esse exemplo mostra como Zustand pode ser utilizado para gerenciar estados globais sem necessidade de Redux ou contextos complexos.
E o React Query?
React Query é uma biblioteca focada no gerenciamento de estado assíncrono, especialmente no que diz respeito a dados provenientes de requisições HTTP. Ela simplifica o processo de fetching, caching, sincronização e atualização de dados, proporcionando uma experiência mais fluida tanto para desenvolvedores quanto para usuários finais.
Principais Benefícios:
- Revalidação Automática: Atualiza os dados automaticamente quando o usuário retorna ao foco da aplicação ou quando ocorrem mudanças relevantes.
- Redução de Código Complexo: Elimina a necessidade de múltiplos hooks e estados locais para gerenciar requisições, centralizando a lógica de dados.
- Melhoria de Performance: Com técnicas de caching e revalidação, proporciona uma aplicação mais rápida e responsiva.
Exemplo Simples de React Query
Aqui está um exemplo básico de como usar React Query para buscar dados de uma API:
import { useQuery } from '@tanstack/react-query'; import axios from 'axios'; // Função para buscar dados da API const fetchUsers = async () => { const response = await axios.get('https://jsonplaceholder.typicode.com/users'); return response.data; }; function UserList() { const { data: users, error, isLoading } = useQuery(['users'], fetchUsers); if (isLoading) return <p>Carregando usuários...</p>; if (error) return <p>Erro ao carregar usuários.</p>; return ( <div> <h2>Lista de Usuários</h2> <ul> {users.map((user) => ( <li key={user.id}>{user.name}</li> ))} </ul> </div> ); } export default UserList;
Esse exemplo mostra como React Query pode ser utilizado para buscar dados de forma eficiente, com cache automático e revalidação sempre que necessário.
Zustand vs. React Query: Estados Diferentes
Uma diferença fundamental entre essas bibliotecas é a forma como lidam com o estado:
✅ Zustand → Gerencia estado manipulável no cliente, ou seja, dados que podem ser alterados diretamente na interface do usuário sem precisar buscar no servidor. Isso inclui estados como temas, filtros, seleções e preferências do usuário.
✅ React Query → Gerencia estado imutável do servidor, ou seja, dados provenientes de uma API externa que devem ser tratados como fontes de verdade (source of truth). Ele não altera diretamente esses dados, mas faz caching, sincronização e revalidação automática para garantir que estejam sempre atualizados.
Exemplo Integrado de Zustand e React Query
Aqui está um exemplo de como usar as duas bibliotecas juntas:
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import { create } from 'zustand'; import axios from 'axios'; // Criando a store com Zustand const useUserStore = create((set) => ({ selectedUser: null, setSelectedUser: (user) => set({ selectedUser: user }), })); // Hook personalizado que combina Zustand e React Query const useUsers = () => { const queryClient = useQueryClient(); const { data: users, error, isLoading } = useQuery(['users'], async () => { const response = await axios.get('https://jsonplaceholder.typicode.com/users'); return response.data; }); const mutationAddUser = useMutation( async (newUser) => { const response = await axios.post('https://jsonplaceholder.typicode.com/users', newUser); return response.data; }, { onSuccess: () => { queryClient.invalidateQueries(['users']); }, } ); const mutationDeleteUser = useMutation( async (userId) => { await axios.delete(`https://jsonplaceholder.typicode.com/users/${userId}`); }, { onSuccess: () => { queryClient.invalidateQueries(['users']); }, } ); const setSelectedUser = useUserStore((state) => state.setSelectedUser); const selectedUser = useUserStore((state) => state.selectedUser); return { users, error, isLoading, selectedUser, setSelectedUser, mutationAddUser, mutationDeleteUser }; }; // Componente de Lista de Usuários function UserList() { const { users, error, isLoading, setSelectedUser, mutationAddUser, mutationDeleteUser } = useUsers(); if (isLoading) return <p>Carregando usuários...</p>; if (error) return <p>Erro ao carregar usuários.</p>; return ( <div> <h2>Lista de Usuários</h2> <button onClick={() => mutationAddUser.mutate({ name: 'Novo Usuário', email: 'novo@email.com' })}> Adicionar Usuário </button> <ul> {users.map((user) => ( <li key={user.id}> <span onClick={() => setSelectedUser(user)}>{user.name}</span> <button onClick={() => mutationDeleteUser.mutate(user.id)}>Deletar</button> </li> ))} </ul> </div> ); } // Componente de Detalhes do Usuário function UserDetails() { const { selectedUser } = useUsers(); if (!selectedUser) return <p>Nenhum usuário selecionado.</p>; return ( <div> <h2>Detalhes do Usuário</h2> <p><strong>Nome:</strong> {selectedUser.name}</p> <p><strong>Email:</strong> {selectedUser.email}</p> </div> ); } export { useUsers, UserList, UserDetails };
Esse exemplo demonstra como Zustand pode armazenar o usuário selecionado, enquanto React Query gerencia a busca e o cache dos usuários de uma API.
O que esse exemplo inclui ?
- Fetching de usuários com React Query (useQuery).
- Adição de usuários usando useMutation e invalidateQueries para atualizar a lista.
- Remoção de usuários com useMutation e invalidateQueries para garantir atualização do cache.
- Gerenciamento de usuário selecionado usando Zustand (useUserStore).
Benefícios da Abordagem
- Centraliza o gerenciamento do estado sem precisar de Redux.
- Melhor performance através do caching e invalidation do React Query.
- Código mais organizado e reutilizável com o uso de hooks personalizados.
Conclusão
A combinação de Zustand e React Query representa uma abordagem eficiente para o gerenciamento de estado em aplicações React. Use Zustand para armazenar estados da UI (como modal aberto/fechado, inputs do usuário, estados globais da aplicação). Use React Query para estados que vêm do servidor (como listas de produtos, usuários, posts etc.), aproveitando seus mecanismos de cache e atualização automática. A combinação dos dois evita re-renders desnecessários e mantém o código mais performático.