//
Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Divida stores grandes em slices focados e, em seguida, combine-os em um único store. Cada slice define seu próprio estado e ações de forma independente.
// stores/slices/user-slice.ts
import { StateCreator } from "zustand";
export interface UserSlice {
user: { name: string; email: string } | null;
setUser: (user: { name: string; email: string }) => void;
clearUser: () => void;
}
export const createUserSlice: StateCreator<UserSlice> = (set) => ({
user: null,
setUser: (user) => set({ user }),
clearUser: () => set({ user: null }),
});// stores/slices/theme-slice.ts
import { StateCreator } from "zustand";
export interface ThemeSlice {
theme: "light" | "dark";
toggleTheme: () => void;
}
export const createThemeSlice: StateCreator<ThemeSlice> = (set) => ({
theme: "light",
toggleTheme: () => set((s) => ({ theme: s.theme === "light" ? "dark" : "light" })),
});// stores/app-store.ts
import { create } from "zustand";
import { createUserSlice, UserSlice } from "./slices/user-slice";
import { createThemeSlice, ThemeSlice } from "./slices/theme-slice";
type AppStore = UserSlice & ThemeSlice;
export const useAppStore = create<AppStore>()((...args) => ({
...createUserSlice(...args),
...createThemeSlice(...args),
}));// stores/slices/cart-slice.ts
import { StateCreator } from "zustand";
import type { AuthSlice } from "./auth-slice";
interface CartItem {
id: string;
name: string;
price: number;
quantity: number;
}
export interface CartSlice {
items: CartItem[];
addItem: (item: Omit<CartItem, "quantity">) => void;
removeItem: (id: string) => void;
getCartTotal: () => number;
checkout: () => Promise<void>;
}
// Acesso entre slices através do tipo do store combinado
export const createCartSlice: StateCreator<
CartSlice & AuthSlice, // Tipo completo do store
[], // Sem mutators (middleware)
[], // Sem mutators
CartSlice // Tipo deste slice
> = (set, get) => ({
items: [],
addItem: (item) =>
set((state) => {
const existing = state.items.find((i) => i.id === item.id);
if (existing) {
return {
items: state.items.map((i) =>
i.id === item.id ? { ...i, quantity: i.quantity + 1 } : i
),
};
}
return { items: [...state.items, { ...item, quantity: 1 }] };
}),
removeItem: (id) =>
set((state) => ({ items: state.items.filter((i) => i.id !== id) })),
getCartTotal: () => get().items.reduce((sum, i) => sum + i.price * i.quantity, 0),
checkout: async () => {
const { items } = get();
const token = get().token; // Acessando o estado do AuthSlice
await fetch("/api/checkout", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ items }),
});
set({ items: [] });
},
});// stores/slices/auth-slice.ts
import { StateCreator } from "zustand";
export interface AuthSlice {
token: string | null;
login: (email: string, password: string) => Promise<void>;
logout: () => void;
}
export const createAuthSlice: StateCreator<AuthSlice> = (set) => ({
token: null,
login: async (email, password) => {
const res = await fetch("/api/auth/login", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password }),
});
const { token } = await res.json();
set({ token });
},
logout: () => set({ token: null }),
});// stores/app-store.ts
import { create } from "zustand";
import { createCartSlice, CartSlice } from "./slices/cart-slice";
import { createAuthSlice, AuthSlice } from "./slices/auth-slice";
type AppStore = CartSlice & AuthSlice;
export const useAppStore = create<AppStore>()((...args) => ({
...createAuthSlice(...args),
...createCartSlice(...args),
}));// components/cart.tsx
"use client";
import { useAppStore } from "@/stores/app-store";
export function Cart() {
const items = useAppStore((s) => s.items);
const removeItem = useAppStore((s) => s.removeItem);
const checkout = useAppStore((s) => s.checkout);
const getCartTotal = useAppStore((s) => s.getCartTotal);
return (
<div>
{items.map((item) => (
<div key={item.id}>
<span>{item.name} x{item.quantity}</span>
<span>${(item.price * item.quantity).toFixed(2)}</span>
<button onClick={() => removeItem(item.id)}>Remover</button>
</div>
))}
<p>Total: ${getCartTotal().toFixed(2)}</p>
<button onClick={checkout}>Finalizar Compra</button>
</div>
);
}StateCreator que retorna um objeto de estado parcial.create. O store resultante possui todas as propriedades de todos os slices.StateCreator<CombinedStore, [], [], SliceType> permite que um slice acesse o estado de outros slices via get().set e get em cada slice referem-se ao store combinado, não apenas ao slice.set/get.Slice com middleware:
import { devtools, persist } from "zustand/middleware";
const useStore = create<AppStore>()(
devtools(
persist(
(...args) => ({
...createCartSlice(...args),
...createAuthSlice(...args),
}),
{ name: "app-store" }
)
)
);Stores independentes em vez de slices:
// Stores separados para domínios verdadeiramente independentes
export const useCartStore = create<CartState>((set) => ({ ... }));
export const useAuthStore = create<AuthState>((set) => ({ ... }));
// Acesso entre stores
const token = useAuthStore.getState().token;StateCreator<FullStore, Middleware, Middleware, SliceType> é o tipo chave para slices que necessitam de acesso entre slices.StateCreator<SliceType> é suficiente.import { StateCreator } from "zustand";
// Slice simples (sem acesso entre slices)
export const createThemeSlice: StateCreator<ThemeSlice> = (set) => ({ ... });
// Slice com acesso entre slices
export const createCartSlice: StateCreator<
CartSlice & AuthSlice, // Tipo completo do store
[], // Sem mutators (middleware)
[], // Sem mutators
CartSlice // Tipo deste slice
> = (set, get) => ({ ... });isLoading, um sobrescreverá o outro silenciosamente.StateCreator. Esquecer de incluir o tipo de outro slice significa que get() não terá suas propriedades.StateCreator com quatro parâmetros de tipo é necessário para slices que usam middleware ou acesso entre slices. A forma de dois parâmetros não funciona.| Abordagem | Prós | Contras |
|---|---|---|
| Padrão de Slice | Modular, acesso entre slices possível | Genéricos TypeScript complexos |
| Stores separados | Verdadeiramente independentes, tipos mais simples | Sem estado compartilhado, acesso entre stores requer getState() |
| Store grande único | Nenhuma combinação necessária | Difícil de manter em escala |
| Slices do Redux Toolkit | Padrão familiar, ferramentas integradas | Boilerplate do Redux |
StateCreator que retorna um objeto de estado parcial.create.type AppStore = UserSlice & ThemeSlice;
export const useAppStore = create<AppStore>()((...args) => ({
...createUserSlice(...args),
...createThemeSlice(...args),
}));get(), que referencia o store combinado.StateCreator<FullStore, [], [], SliceType> para que o TypeScript conheça as propriedades do outro slice.immer pode às vezes ser usado no nível do slice, mas isso é raro.const useStore = create<AppStore>()(
devtools(persist((...args) => ({
...createCartSlice(...args),
...createAuthSlice(...args),
}), { name: "app-store" }))
);getState() para acesso entre stores.set/get do store combinado.import { StateCreator } from "zustand";
const createThemeSlice: StateCreator<ThemeSlice> = (set) => ({
theme: "light",
toggleTheme: () => set((s) => ({
theme: s.theme === "light" ? "dark" : "light",
})),
});const createCartSlice: StateCreator<
CartSlice & AuthSlice, // Tipo completo do store
[], // Mutators de entrada
[], // Mutators de saída
CartSlice // Tipo deste slice
> = (set, get) => ({
// get().token agora é tipado do AuthSlice
});type AppStore = SliceA & SliceB & SliceC.create<AppStore>().Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥