00
:
00
:
00
:
00
Corso SEO AI - Usa SEOEMAIL al checkout per il 30% di sconto

Slices Pattern

Perché gli Slices

Quando lo store cresce, mantenere tutto in un unico file diventa ingestibile. Lo slices pattern permette di dividere lo stato in moduli separati che vengono poi combinati in un unico store.

StateCreator Type

Zustand espone il tipo StateCreator che definisce la firma di una “fetta” di store.

import { StateCreator } from 'zustand'

interface AuthSlice {
  user: string | null
  token: string | null
  login: (user: string, token: string) => void
  logout: () => void
}

const createAuthSlice: StateCreator<AuthSlice> = (set) => ({
  user: null,
  token: null,
  login: (user, token) => set({ user, token }),
  logout: () => set({ user: null, token: null }),
})

Definire Piu’ Slices

interface CartSlice {
  items: { id: string; name: string; qty: number }[]
  addItem: (item: { id: string; name: string }) => void
  clearCart: () => void
}

const createCartSlice: StateCreator<CartSlice> = (set) => ({
  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, qty: i.qty + 1 } : i
          ),
        }
      }
      return { items: [...state.items, { ...item, qty: 1 }] }
    }),
  clearCart: () => set({ items: [] }),
})

Combinare gli Slices

Unisci tutti gli slices in un unico store usando lo spread operator.

import { create } from 'zustand'

type AppStore = AuthSlice & CartSlice

const useAppStore = create<AppStore>()((...args) => ({
  ...createAuthSlice(...args),
  ...createCartSlice(...args),
}))

Lo store risultante ha tutte le proprietà e le azioni di entrambi gli slices.

function Header() {
  const user = useAppStore((s) => s.user)
  const itemCount = useAppStore((s) => s.items.length)

  return (
    <header>
      <span>{user ?? 'Ospite'}</span>
      <span>Carrello: {itemCount}</span>
    </header>
  )
}

Slices che Accedono ad Altri Slices

Uno slice puo’ leggere o aggiornare lo stato di un altro slice. Specifica l’intero tipo dello store come secondo parametro generico di StateCreator.

interface NotificationSlice {
  notifications: string[]
  notify: (msg: string) => void
}

// Il secondo generico e' lo store completo, il terzo e' un eventuale middleware
const createCartSliceWithNotify: StateCreator<
  CartSlice & NotificationSlice,
  [],
  [],
  CartSlice
> = (set, get) => ({
  items: [],
  addItem: (item) => {
    set((state) => ({
      items: [...state.items, { ...item, qty: 1 }],
    }))
    // Accede allo slice notifications
    get().notify(`${item.name} aggiunto al carrello`)
  },
  clearCart: () => set({ items: [] }),
})

Organizzazione del Codice

La struttura consigliata per progetto di medie/grandi dimensioni.

src/
  stores/
    slices/
      authSlice.ts        # StateCreator<AuthSlice>
      cartSlice.ts        # StateCreator<CartSlice>
      uiSlice.ts          # StateCreator<UISlice>
    useAppStore.ts        # create() che combina tutti gli slices
    types.ts              # tipi condivisi tra slices

File useAppStore.ts

import { create } from 'zustand'
import { devtools } from 'zustand/middleware'
import { createAuthSlice, AuthSlice } from './slices/authSlice'
import { createCartSlice, CartSlice } from './slices/cartSlice'
import { createUISlice, UISlice } from './slices/uiSlice'

export type AppStore = AuthSlice & CartSlice & UISlice

export const useAppStore = create<AppStore>()(
  devtools((...args) => ({
    ...createAuthSlice(...args),
    ...createCartSlice(...args),
    ...createUISlice(...args),
  }))
)

Ogni slice e’ testabile in isolamento, facile da navigare e con responsabilita’ chiare. Quando lo store diventa complesso, questo pattern mantiene il codice organizzato e manutenibile.