R
Interview Deckreact · next.js · senior/staff
0·0·122
Estado Global

Zustand — Guia Completo

Store simples, seletores, middleware (persist, devtools, immer), e padrões avançados.

Zustand é uma lib minimalista de estado global (1kb). Store é criado com create(), actions ficam no mesmo objeto que o estado, seletores evitam re-renders desnecessários. Middleware: persist (localStorage automático), devtools (Redux DevTools), immer (mutação sintática). Sem Provider, sem boilerplate, sem Context.

Problema
Redux para uma funcionalidade simples exige: types, actions, reducers, selectors, thunks — 100 linhas para guardar um boolean. Context API com re-render de todos os consumers.
Solução
Zustand: create() com estado e actions inline. Seletor para consumir só o que precisa (previne re-render). persist middleware para persistir no localStorage com uma linha. devtools para debugging com Redux DevTools.
Tip
Seletores são críticos: `useStore(s => s.count)` re-renderiza só quando count muda. `useStore()` sem seletor re-renderiza quando qualquer parte do store muda. Sempre use seletor granular.
import { create } from 'zustand'
import { persist, devtools } from 'zustand/middleware'
import { immer } from 'zustand/middleware/immer'

interface CartStore {
  items: CartItem[]
  addItem: (item: CartItem) => void
  removeItem: (id: string) => void
  total: number
}

export const useCartStore = create<CartStore>()(
  devtools(
    persist(
      immer((set, get) => ({
        items: [],

        addItem: (item) => set(state => {
          state.items.push(item) // Immer: mutação segura
        }),

        removeItem: (id) => set(state => {
          state.items = state.items.filter(i => i.id !== id)
        }),

        get total() {
          return get().items.reduce((s, i) => s + i.price, 0)
        },
      })),
      { name: 'cart-storage' } // persist config
    ),
    { name: 'CartStore' } // devtools name
  )
)

// Consumir com seletor granular
function CartBadge() {
  // ✅ só re-renderiza quando items.length muda
  const count = useCartStore(s => s.items.length)
  return <span>{count}</span>
}

function AddButton({ item }) {
  // ✅ actions são estáveis (referência não muda)
  const addItem = useCartStore(s => s.addItem)
  return <button onClick={() => addItem(item)}>Adicionar</button>
}
Q.Como dividir uma store Zustand grande?
A.Slice pattern: criar múltiplas slices e combinar: `create<State>()((...args) => ({ ...createAuthSlice(...args), ...createCartSlice(...args) }))`. Ou criar stores separadas por domínio — Zustand não tem store única obrigatória, ao contrário do Redux.
Q.Como persistir estado com Zustand?
A.Middleware persist: `create(persist((set) => ({ count: 0, inc: () => set(s => ({count: s.count+1})) }), { name: "counter-storage" }))`. Persiste no localStorage por padrão. Customizar storage (sessionStorage, IndexedDB), partializar quais campos persistir, e migrar state entre versões.
Q.Zustand funciona com Server Components?
A.Não diretamente — Zustand é client-side (usa window/browser APIs). Inicializar store com dados do servidor via prop do Client Component ou via useHydrateAtoms (Jotai). No Next.js App Router: criar uma instância por request no servidor usando a forma funcional do create.
70/122← →