diogodev_
Conteúdo

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