//
Busque em todas as páginas da documentação
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
Use funções seletoras com useStore(selector) para se inscrever em fatias específicas do estado. Os componentes só re-renderizam quando o valor selecionado muda, prevenindo re-renderizações desnecessárias.
"use client";
import { useCartStore } from "@/stores/cart-store";
function CartBadge() {
// Só re-renderiza quando o array de itens muda
const itemCount = useCartStore((state) => state.items.length);
return <span className="badge">{itemCount}</span>;
}
function CartTotal() {
// Só re-renderiza quando o total muda
const total = useCartStore((state) =>
state.items.reduce((sum, item) => sum + item.price * item.quantity, 0)
);
return <span>${total.toFixed(2)}</span>;
}// stores/dashboard-store.ts
import { create } from "zustand";
interface DashboardStore {
user: { name: string; avatar: string };
notifications: { id: string; message: string }[];
theme: "light" | "dark";
sidebarOpen: boolean;
toggleSidebar: () => void;
toggleTheme: () => void;
addNotification: (message: string) => void;
}
export const useDashboardStore = create<DashboardStore>((set) => ({
user: { name: "Jane", avatar: "/avatar.png" },
notifications: [],
theme: "light",
sidebarOpen: true,
toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
toggleTheme: () => set((s) => ({ theme: s.theme === "light" ? "dark" : "light" })),
addNotification: (message) =>
set((s) => ({
notifications: [...s.notifications, { id: crypto.randomUUID(), message }],
})),
}));// components/header.tsx
"use client";
import { useDashboardStore } from "@/stores/dashboard-store";
import { useShallow } from "zustand/react/shallow";
// Ruim: re-renderiza em QUALQUER mudança de estado
function HeaderBad() {
const store = useDashboardStore();
return <div>{store.user.name}</div>;
}
// Bom: só re-renderiza quando user.name muda
function HeaderGood() {
const userName = useDashboardStore((s) => s.user.name);
return <div>{userName}</div>;
}
// Bom: múltiplos valores com comparação rasa (shallow)
function HeaderWithMultiple() {
const { name, avatar } = useDashboardStore(
useShallow((s) => ({ name: s.user.name, avatar: s.user.avatar }))
);
return (
<div>
<img src={avatar} alt={name} />
<span>{name}</span>
</div>
);
}
// Bom: selecionando múltiplos primitivos com array useShallow
function NotificationBar() {
const [notifications, theme] = useDashboardStore(
useShallow((s) => [s.notifications, s.theme])
);
return (
<div className={theme}>
{notifications.length} notificações
</div>
);
}===) por padrão para comparar o valor selecionado anterior e o próximo.{} !== {}.useShallow de zustand/react/shallow realiza uma comparação rasa (shallow) no objeto ou array selecionado, prevenindo re-renderizações quando propriedades individuais não mudaram.Geração automática de seletores:
import { create } from "zustand";
import { StoreApi, UseBoundStore } from "zustand";
type WithSelectors<S> = S extends { getState: () => infer T }
? S & { use: { [K in keyof T]: () => T[K] } }
: never;
function createSelectors<S extends UseBoundStore<StoreApi<object>>>(store: S) {
const storeIn = store as WithSelectors<typeof store>;
storeIn.use = {} as any;
for (const key of Object.keys(storeIn.getState())) {
(storeIn.use as any)[key] = () => storeIn((s: any) => s[key]);
}
return storeIn;
}
// Uso
const useCounterStoreBase = create<CounterState>((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
}));
export const useCounterStore = createSelectors(useCounterStoreBase);
// Seletores gerados automaticamente
const count = useCounterStore.use.count();
const increment = useCounterStore.use.increment();Seletor derivado memorizado:
import { useMemo } from "react";
function ExpensiveList() {
const items = useCartStore((s) => s.items);
// Memoriza computação cara
const sortedItems = useMemo(
() => [...items].sort((a, b) => b.price - a.price),
[items]
);
return <ul>{sortedItems.map((item) => <li key={item.id}>{item.name}</li>)}</ul>;
}Seleção de ações (referência estável):
// Ações nunca mudam, então este seletor nunca dispara uma re-renderização
const increment = useCounterStore((s) => s.increment);
const reset = useCounterStore((s) => s.reset);useShallow preserva o tipo de retorno do seletor.// resultado é tipado como number
const count = useStore((s: StoreState) => s.count);
// resultado é tipado como { name: string; email: string }
const user = useStore(
useShallow((s: StoreState) => ({ name: s.user.name, email: s.user.email }))
);useShallow causa re-renderizações a cada mudança de estado: (s) => ({ a: s.a, b: s.b }) cria um novo objeto a cada vez.useShallow compara apenas um nível de profundidade. Objetos ou arrays aninhados ainda usam igualdade de referência para seus filhos.(s) => s.increment nunca dispara re-renderizações porque as referências de função são estáveis no Zustand. Isso é seguro e recomendado.useMemo após selecionar os dados brutos em vez disso.| Abordagem | Prós | Contras |
|---|---|---|
| Seletores individuais | Preciso, re-renderizações mínimas | Verboso para muitos valores |
| useShallow | Seleção de múltiplos valores, API simples | Apenas comparação rasa (shallow) |
| Seletores gerados automaticamente | Zero boilerplate para acesso por campo | Mágica, mais difícil de depurar |
| Sem seletor (estado completo) | Simples | Re-renderiza a cada mudança |
===) para comparar o valor selecionado anterior e o próximo.{} !== {}.useShallow quando seu seletor retornar um objeto ou array com múltiplos valores.zustand/react/shallow.import { useShallow } from "zustand/react/shallow";
const { name, avatar } = useStore(
useShallow((s) => ({ name: s.user.name, avatar: s.user.avatar }))
);{} !== {}, o Zustand o vê como um novo valor e dispara uma re-renderização.useShallow para comparar propriedades individuais em vez disso.useShallow compara apenas um nível de profundidade.createSelectors gera hooks por campo: useStore.use.count().useMemo para computações derivadas caras.const items = useCartStore((s) => s.items);
const sorted = useMemo(() => [...items].sort(...), [items]);if ou após um return condicional.useShallow preserva o tipo de retorno do seletor.// resultado é tipado como number
const count = useStore((s: StoreState) => s.count);type WithSelectors<S> = S extends { getState: () => infer T }
? S & { use: { [K in keyof T]: () => T[K] } }
: never;useShallow seleciona múltiplos valores, mas adiciona uma passagem de comparação rasa (shallow).Revisado por Chris St. John·Última atualização: 7 de jul. de 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥