State Management with Zustand

Loading

Zustand is a fast and minimal state management library for React. It provides a simple and flexible way to manage global state in React applications. Unlike other state management solutions like Redux, Zustand is less opinionated, has a smaller API, and integrates seamlessly into your React components.

Zustand is designed to be simple to use and can scale well with more complex applications. It’s ideal for managing global state that needs to be accessed by many components without the overhead of more complex libraries.


1. What is Zustand?

Zustand (German for “state”) is a state management library that leverages the React Context API under the hood but is designed to be much simpler and more performant. Zustand allows you to create a store where you can define and manage state and update that state using actions.

It’s based on the concept of “hooks”, and allows you to define a global store that can be accessed across your entire application with minimal boilerplate.


2. Installing Zustand

To get started with Zustand, first install it in your project using npm or yarn:

npm install zustand

Or with yarn:

yarn add zustand

3. Creating a Store in Zustand

In Zustand, a store is created by calling the create function, which returns a function that holds the state of your application. You define your store by passing an object to the create function, where the state and actions are defined.

Here’s a basic example of creating a store:

// store.js
import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
}));

export default useStore;

Explanation:

  • useStore: This is the custom hook that Zustand provides. You call this hook in your React components to access and modify the store’s state.
  • set: A function provided by Zustand that updates the store’s state.
  • State and Actions: You define the initial state (count: 0) and actions (increase and decrease) that update the state.

4. Using Zustand Store in React Components

Once you’ve created a store, you can access the store’s state and call actions from any component by using the useStore hook.

// Counter.js
import React from 'react';
import useStore from './store';

const Counter = () => {
  const { count, increase, decrease } = useStore();

  return (
    <div>
      <h2>Count: {count}</h2>
      <button onClick={increase}>Increment</button>
      <button onClick={decrease}>Decrement</button>
    </div>
  );
};

export default Counter;

Explanation:

  • useStore(): By calling useStore(), the component will re-render whenever the count in the store changes.
  • Actions: You can call actions like increase and decrease directly from the store to update the state.

5. Zustand with Multiple States

You can manage multiple pieces of state in a single store. Zustand is flexible and allows you to define any state logic you want.

For example, here’s how you can manage multiple state slices (pieces of state) within the same store:

// store.js
import create from 'zustand';

const useStore = create((set) => ({
  count: 0,
  user: null,
  increase: () => set((state) => ({ count: state.count + 1 })),
  decrease: () => set((state) => ({ count: state.count - 1 })),
  setUser: (user) => set({ user }),
}));

export default useStore;

Explanation:

  • The store now has multiple pieces of state: count and user.
  • Actions like setUser allow you to update specific pieces of state.

6. Zustand with Async Actions (e.g., Fetching Data)

Zustand is also great for managing async actions. You can handle data fetching, and once the data is available, you can update the store state accordingly.

For example, here’s how you can manage fetching data using Zustand:

// store.js
import create from 'zustand';

const useStore = create((set) => ({
  data: null,
  isLoading: false,
  error: null,
  fetchData: async (url) => {
    set({ isLoading: true });
    try {
      const response = await fetch(url);
      const data = await response.json();
      set({ data, isLoading: false });
    } catch (error) {
      set({ error: error.message, isLoading: false });
    }
  },
}));

export default useStore;

Explanation:

  • fetchData: An asynchronous function that makes a network request and updates the state with the fetched data or an error.
  • isLoading and error: Used to track the loading state and any errors that occur during the data fetching process.

7. Using Zustand in React Components (with Async Actions)

Now, let’s use the store to fetch data in a component:

// DataFetcher.js
import React, { useEffect } from 'react';
import useStore from './store';

const DataFetcher = () => {
  const { data, isLoading, error, fetchData } = useStore();

  useEffect(() => {
    fetchData('https://jsonplaceholder.typicode.com/posts');
  }, [fetchData]);

  if (isLoading) return <p>Loading...</p>;
  if (error) return <p>Error: {error}</p>;

  return (
    <div>
      <h2>Data:</h2>
      <ul>
        {data &&
          data.map((item) => (
            <li key={item.id}>
              {item.title}
            </li>
          ))}
      </ul>
    </div>
  );
};

export default DataFetcher;

Explanation:

  • fetchData: We call this function inside the useEffect hook to fetch data when the component mounts.
  • Loading and Error States: The component displays loading or error messages based on the state managed by Zustand.

8. Zustand with Middleware

Zustand supports middleware to extend the functionality of your store. For example, you can use middleware for persistence (saving state to localStorage), logging, or even for debugging.

Here’s an example of using middleware to persist state to localStorage:

npm install zustand/middleware
// store.js
import create from 'zustand';
import { persist } from 'zustand/middleware';

const useStore = create(persist(
  (set) => ({
    count: 0,
    increase: () => set((state) => ({ count: state.count + 1 })),
    decrease: () => set((state) => ({ count: state.count - 1 })),
  }),
  {
    name: 'count-storage', // name of the localStorage key
  }
));

export default useStore;

Explanation:

  • persist middleware automatically syncs the store with localStorage, so the state is retained even after page reloads.

9. Benefits of Using Zustand

  • Minimalistic API: Zustand has a very simple API, making it easy to learn and use.
  • No Boilerplate: There’s no need for actions, reducers, or connect functions like Redux.
  • Performance: Zustand is fast and lightweight, as it uses React’s native useState hook for state management.
  • Flexibility: Zustand allows you to manage multiple states in a single store and supports asynchronous logic, making it flexible enough for different use cases.
  • Easy Debugging: Zustand provides hooks to track the state and actions, making it easier to debug and maintain your application.

Leave a Reply

Your email address will not be published. Required fields are marked *