Module: Zustand State Management

Using Zustand with Components

Zustand State Management with React Components

Zustand is a small, fast, and scalable bearbones state-management solution using simplified flux principles. It's a great alternative to Redux or Context API, especially for smaller to medium-sized applications. This guide demonstrates how to use Zustand with React components.

1. Installation

First, install Zustand:


npm install zustand
# or
yarn add zustand
# or
pnpm add zustand

2. Creating a Store

A Zustand store is created using the create function. This function takes a single argument: a state object with setter functions.

import { create } from 'zustand';

const useBearStore = create((set) => ({
  bears: 0,
  addBear: () => set((state) => ({ bears: state.bears + 1 })),
  removeBear: () => set((state) => ({ bears: state.bears - 1 })),
  resetBears: () => set({ bears: 0 }),
}));

export default useBearStore;

Explanation:

  • create((set) => ({ ... })): This creates the store. The set function is the core of Zustand. It's used to update the state.
  • bears: 0: This is the initial state of the bears property.
  • addBear: () => set((state) => ({ bears: state.bears + 1 })): This is a setter function. It takes the current state (state) and returns a new state object with the bears property incremented. Using the functional update form (state) => ({ ... }) is recommended for updates based on the previous state.
  • removeBear: () => set((state) => ({ bears: state.bears - 1 })): Similar to addBear, but decrements the bears count.
  • resetBears: () => set({ bears: 0 }): Resets the bears count to 0.

3. Using the Store in Components

To use the store in a React component, you call the store function (e.g., useBearStore) as a hook. This returns the store's state and any setter functions.

import React from 'react';
import useBearStore from './store'; // Assuming store.js contains the code from step 2

function BearCounter() {
  const bears = useBearStore((state) => state.bears);
  const addBear = useBearStore((state) => state.addBear);
  const removeBear = useBearStore((state) => state.removeBear);
  const resetBears = useBearStore((state) => state.resetBears);

  return (
    <div>
      <h1>Bears: {bears}</h1>
      <button onClick={addBear}>Add Bear</button>
      <button onClick={removeBear}>Remove Bear</button>
      <button onClick={resetBears}>Reset Bears</button>
    </div>
  );
}

export default BearCounter;

Explanation:

  • const bears = useBearStore((state) => state.bears);: This selectively extracts the bears property from the store's state. This is a key optimization in Zustand – components only re-render when the properties they subscribe to change.
  • const addBear = useBearStore((state) => state.addBear);: This selectively extracts the addBear setter function.
  • const removeBear = useBearStore((state) => state.removeBear);: This selectively extracts the removeBear setter function.
  • const resetBears = useBearStore((state) => state.resetBears);: This selectively extracts the resetBears setter function.
  • The component then uses these values to display the bear count and provide buttons to modify it.

4. Selective State and Actions (Important Optimization)

As demonstrated above, Zustand encourages selective subscription. Instead of subscribing to the entire store state, components only subscribe to the specific parts of the state they need. This significantly improves performance by minimizing unnecessary re-renders.

Bad (Subscribing to the entire state):

const store = useBearStore();
const bears = store.bears;
const addBear = store.addBear;

Good (Selective Subscription):

const bears = useBearStore((state) => state.bears);
const addBear = useBearStore((state) => state.addBear);

5. Multiple Stores

You can create multiple stores to manage different parts of your application's state.

// store/userStore.js
import { create } from 'zustand';

const useUserStore = create((set) => ({
  name: '',
  email: '',
  setName: (name) => set({ name }),
  setEmail: (email) => set({ email }),
}));

export default useUserStore;

// store/productStore.js
import { create } from 'zustand';

const useProductStore = create((set) => ({
  products: [],
  addProduct: (product) => set((state) => ({ products: [...state.products, product] })),
}));

export default useProductStore;

Then, use them in your components as needed:

import React from 'react';
import useUserStore from './store/userStore';
import useProductStore from './store/productStore';

function MyComponent() {
  const userName = useUserStore((state) => state.name);
  const addProduct = useProductStore((state) => state.addProduct);

  return (
    <div>
      <p>User: {userName}</p>
      <button onClick={() => addProduct({ id: 1, name: 'Example Product' })}>Add Product</button>
    </div>
  );
}

export default MyComponent;

6. Middleware (Advanced)

Zustand supports middleware for logging, persistence, or other side effects.

import { create } from 'zustand';
import { persist } from 'zustand/middleware';

const useMyStore = create(
  persist(
    (set) => ({
      count: 0,
      increment: () => set((state) => ({ count: state.count + 1 })),
    }),
    {
      name: 'my-store', // unique name for persistence
      getStorage: () => localStorage, // optional: custom storage
    }
  )
);

export default useMyStore;

This example uses the persist middleware to save the count state to localStorage.

Key Benefits of Zustand:

  • Simplicity: Easy to learn and use.
  • Minimal Boilerplate: Less code compared to Redux.
  • Performance: Selective subscription minimizes re-renders.
  • Scalability: Can handle complex state management needs.
  • Flexibility: Supports middleware for advanced features.
  • Small Bundle Size: Very lightweight library.