Codelynx.dev
Posts

Zustand : Le guide complet pour gérer ton state en React

12/02/2025 Melvynx

Tu cherches une solution simple et efficace pour gérer l'état de ton application React ? Zustand est là pour ça ! C'est un gestionnaire d'état minimaliste mais puissant qui va révolutionner ta façon de gérer les données.

Pourquoi Zustand ?

Imagine que tu construis une maison. Redux, c'est comme avoir un énorme entrepôt avec des processus complexes pour gérer chaque meuble. Zustand, c'est comme avoir un système de rangement simple mais efficace, directement dans ta maison.

Zustand brille par sa simplicité :

  • Pas de Provider à wrapper autour de ton app
  • Une API simple basée sur les hooks
  • Compatible TypeScript
  • Performances optimisées

Comment utiliser Zustand ?

Commençons par un exemple simple :

import { create } from 'zustand'

type Store = {
count: number
increment: () => void
decrement: () => void
}

const useStore = create<Store>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
decrement: () => set((state) => ({ count: state.count - 1 })),
}))

export default function App() {
const { count, increment, decrement } = useStore()

return (
  <div>
    <h1>Compteur: {count}</h1>
    <button onClick={increment}>+</button>
    <button onClick={decrement}>-</button>
  </div>
)
}

Dans cet exemple :

  1. On crée un store avec create
  2. On définit notre state initial et nos actions
  3. On utilise le hook useStore pour accéder au state

L'état immuable avec Zustand

Un concept important en React est l'immutabilité. Cela signifie qu'on ne modifie jamais directement le state, on crée une nouvelle copie.

Voici un exemple plus complexe :

import { create } from 'zustand'

type Todo = {
id: number
text: string
done: boolean
}

type Store = {
todos: Todo[]
addTodo: (text: string) => void
toggleTodo: (id: number) => void
}

const useStore = create<Store>((set) => ({
todos: [],
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: Date.now(), text, done: false }]
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(todo =>
todo.id === id ? { ...todo, done: !todo.done } : todo
)
}))
}))

export default function App() {
const { todos, addTodo, toggleTodo } = useStore()

return (
  <div>
    <button onClick={() => addTodo("Nouvelle tâche")}>
      Ajouter une tâche
    </button>
    <ul>
      {todos.map(todo => (
        <li 
          key={todo.id}
          onClick={() => toggleTodo(todo.id)}
          style={{ textDecoration: todo.done ? "line-through" : "none" }}
        >
          {todo.text}
        </li>
      ))}
    </ul>
  </div>
)
}

TypeScript avec Zustand

Zustand est conçu pour fonctionner parfaitement avec TypeScript. Voici un exemple plus avancé :

TYPESCRIPT
type User = {
  id: number;
  name: string;
};

type AuthStore = {
  user: User | null;
  isLoading: boolean;
  error: string | null;
  login: (username: string, password: string) => Promise<void>;
  logout: () => void;
};

const useAuthStore = create<AuthStore>((set) => ({
  user: null,
  isLoading: false,
  error: null,
  login: async (username, password) => {
    set({ isLoading: true, error: null });
    try {
      // Simule un appel API
      const user = await api.login(username, password);
      set({ user, isLoading: false });
    } catch (error) {
      set({ error: error.message, isLoading: false });
    }
  },
  logout: () => set({ user: null }),
}));

Optimiser les re-renders avec useShallow

Parfois, tu veux sélectionner plusieurs valeurs du store sans causer de re-render inutile. C'est là qu'intervient useShallow :

import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

type Store = {
user: {
firstName: string
lastName: string
age: number
}
updateAge: (age: number) => void
}

const useStore = create<Store>((set) => ({
user: {
firstName: "John",
lastName: "Doe",
age: 30
},
updateAge: (age) => set((state) => ({
user: { ...state.user, age }
}))
}))

function UserInfo() {
// Utilise useShallow pour éviter les re-renders inutiles
const { firstName, lastName } = useStore(
useShallow((state) => ({
firstName: state.user.firstName,
lastName: state.user.lastName
}))
)

console.log("UserInfo render")

return <div>{firstName} {lastName}</div>
}

export default function App() {
const updateAge = useStore(state => state.updateAge)

return (
  <div>
    <UserInfo />
    <button onClick={() => updateAge(31)}>
      Update Age
    </button>
  </div>
)
}

Dans cet exemple, UserInfo ne se re-rendra pas quand l'âge change, car useShallow compare superficiellement l'objet retourné.

Bonnes pratiques

  1. Sépare tes stores : Crée plusieurs petits stores plutôt qu'un gros
  2. Utilise l'immutabilité : Ne modifie jamais directement le state
  3. TypeScript : Définis bien tes types pour une meilleure maintenabilité
  4. Optimise les re-renders : Utilise useShallow quand nécessaire

Conclusion

Zustand est un excellent choix pour la gestion d'état en React. Il est simple à utiliser, performant et bien pensé. Si tu débutes avec la gestion d'état, c'est probablement le meilleur choix pour commencer !

N'hésite pas à consulter la documentation officielle pour approfondir tes connaissances.