Gerenciamento de estado utilizando Zustand
Gerenciar estado é uma necessidade comum em aplicações React. Embora Context API e Redux sejam opções bastante conhecidas, bibliotecas mais simples como o Zustand vêm ganhando espaço por oferecerem uma API minimalista, tipagem simples e excelente performance.
O que é Zustand?
Zustand é uma biblioteca de gerenciamento de estado para React criada pelos mesmos desenvolvedores do pmndrs.
Ela possui uma API extremamente simples e oferece:
- Pouca configuração;
- Excelente performance;
- Integração com TypeScript;
- Persistência de dados;
- Estados globais e locais;
- Menos boilerplate que Redux.
Um dos seus maiores diferenciais é permitir criar stores utilizando apenas funções.
Instalando a biblioteca
npm install zustand
Ou:
yarn add zustand
Criando a primeira Store
Vamos criar um contador simples.
import { create } from "zustand";
type CounterStore = {
count: number;
increment: () => void;
decrement: () => void;
};
export const useCounterStore = create<CounterStore>(
(set) => ({
count: 0,
increment: () =>
set((state) => ({
count: state.count + 1
})),
decrement: () =>
set((state) => ({
count: state.count - 1
}))
})
);
Toda a lógica fica centralizada em uma store.
Utilizando a Store
Dentro do componente:
function Counter() {
const count = useCounterStore(
state => state.count
);
const increment = useCounterStore(
state => state.increment
);
return (
<>
<h1>{count}</h1>
<button onClick={increment}>
Incrementar
</button>
</>
);
}
Simples e sem necessidade de Providers.
Atualizando estados
Também podemos alterar valores diretamente.
type UserStore = {
name: string;
setName: (name: string) => void;
};
export const useUserStore = create<UserStore>(
(set) => ({
name: "",
setName: (name) =>
set({
name
})
})
);
Uso:
const setName = useUserStore(
state => state.setName
);
setName("Diogo");
Trabalhando com objetos
type User = {
id: number;
name: string;
};
type UserStore = {
user: User | null;
setUser: (user: User) => void;
};
Implementação:
export const useUserStore =
create<UserStore>((set) => ({
user: null,
setUser: (user) =>
set({
user
})
}));
Persistindo dados
Uma funcionalidade muito útil é o middleware persist.
import { persist } from "zustand/middleware";
export const useAuthStore = create(
persist(
(set) => ({
token: "",
setToken: (token: string) =>
set({
token
})
}),
{
name: "auth-storage"
}
)
);
Os dados serão armazenados automaticamente no Local Storage.
Exemplo de autenticação
type AuthStore = {
token: string;
login: (token: string) => void;
logout: () => void;
};
Implementação:
export const useAuthStore =
create<AuthStore>((set) => ({
token: "",
login: (token) =>
set({
token
}),
logout: () =>
set({
token: ""
})
}));
Uso:
const login = useAuthStore(
state => state.login
);
login(jwtToken);
Organização recomendada
Uma estrutura simples:
src
│
├── stores
│ ├── authStore.ts
│ ├── userStore.ts
│ ├── cartStore.ts
│
├── services
│
└── components
Separar stores por domínio facilita a manutenção.
Trabalhando com arrays
Exemplo de carrinho:
type Product = {
id: number;
name: string;
};
type CartStore = {
products: Product[];
addProduct: (
product: Product
) => void;
};
Implementação:
addProduct: (product) =>
set((state) => ({
products: [
...state.products,
product
]
}))
Zustand x Context API
Context APIZustandNecessita ProviderNãoMais re-renderizaçõesMais performáticoIdeal para estados simplesIdeal para estados globaisAPI nativaAPI minimalista
Zustand x Redux
ReduxZustandMais verbosoMais simplesBoilerplate maiorPouca configuraçãoActions e ReducersFunções comunsExcelente para grandes aplicaçõesExcelente para aplicações médias e grandes
Boas práticas
Crie stores por domínio
Evite uma store gigante.
Prefira:
- authStore;
- cartStore;
- userStore.
Utilize seletores
Prefira:
const token = useAuthStore(
state => state.token
);
Ao invés de:
const store = useAuthStore();
Isso evita re-renderizações desnecessárias.
Não coloque tudo no estado global
Estados locais continuam sendo úteis.
Por exemplo:
useState()
Ainda é uma ótima opção para estados simples.
Utilize persist apenas quando necessário
Nem toda informação precisa ser armazenada no Local Storage.
Quando utilizar?
Zustand é excelente para:
- Autenticação;
- Carrinho de compras;
- Tema da aplicação;
- Preferências do usuário;
- Estados compartilhados;
- Dashboards;
- Aplicações React e Next.js.
Vantagens
- API simples;
- Excelente performance;
- Menos boilerplate;
- Fácil integração com TypeScript;
- Persistência de dados;
- Não necessita Provider.
Conclusão
Zustand é uma das soluções mais simples e elegantes para gerenciamento de estado em aplicações React. Sua curva de aprendizado é pequena e a quantidade de código necessária é muito menor quando comparada a soluções mais tradicionais.
Se você procura uma alternativa leve e produtiva para compartilhar estados globais, vale a pena conhecer essa biblioteca.
Saiba mais
- Zustand Documentation
https://zustand.docs.pmnd.rs/ - GitHub do Zustand
https://github.com/pmndrs/zustand - Middleware Persist
https://zustand.docs.pmnd.rs/integrations/persisting-store-data - TypeScript Guide
https://zustand.docs.pmnd.rs/guides/typescript - Selectors and Re-rendering
https://zustand.docs.pmnd.rs/guides/prevent-rerenders-with-use-shallow - Examples
https://zustand.docs.pmnd.rs/guides/updating-state