Managing Data Fetching and Storing with Zustand in React

Zustand is a small, fast, and scalable state management library for React. It provides a straightforward way to manage both global and component-level state, including data fetching and storing. In this post, we'll explore how to use Zustand to manage data fetching and storage in a React application.

Why Use Zustand for State Management?

Zustand is lightweight and easy to use, with a minimal API that offers powerful features. It can replace heavier state management libraries like Redux in many scenarios, particularly for smaller to medium-sized applications.

Setting Up Zustand

First, you need to install Zustand in your project:

npm install zustand

Or if you're using Yarn:

yarn add zustand

Creating a Store

Let's create a store to manage our application's state. We'll start with a simple example that fetches and stores data from an API.

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

In this example, useStore is a custom hook that manages the state. It includes:

  • data: The fetched data.
  • isLoading: A boolean flag indicating if data is being fetched.
  • error: An error message if the fetch fails.
  • fetchData: An asynchronous function to fetch data from a given URL.

Using Zustand in a Component

Now that we have our store, we can use it in a React component. Let's create a component that fetches data from an API and displays it.

import React, { useEffect } from 'react';
import useStore from './store'; // Assume the store is in the same directory
 
export default function DataFetchingComponent() {
  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>
      <h1>Fetched Data</h1>
      {data && data.map((item) => (
        <div key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.body}</p>
        </div>
      ))}
    </div>
  );
}

In this component:

  • We use useEffect to call the fetchData function when the component mounts.
  • The component renders loading, error, or data states based on the values in the store.

Updating and Storing Data

Zustand also makes it easy to update the store's state. Let's extend our example to include functionality for adding and deleting data.

Extending the Store

const useStore = create((set) => ({
  data: null,
  isLoading: false,
  error: null,
 
  fetchData: async (url) => {
    set({ isLoading: true, error: null });
    try {
      const response = await fetch(url);
      const result = await response.json();
      set({ data: result, isLoading: false });
    } catch (error) {
      set({ error: error.message, isLoading: false });
    }
  },
 
  addItem: (newItem) => set((state) => ({ data: [...state.data, newItem] })),
 
  deleteItem: (id) => set((state) => ({ data: state.data.filter((item) => item.id !== id) })),
}));

Using the Extended Store in a Component

Now, we can use the addItem and deleteItem functions in our component:

import React from 'react';
import useStore from './store';
 
export default function DataManipulationComponent() {
  const { data, addItem, deleteItem } = useStore();
 
  const handleAddItem = () => {
    const newItem = { id: data.length + 1, title: 'New Item', body: 'This is a new item.' };
    addItem(newItem);
  };
 
  return (
    <div>
      <h1>Manage Data</h1>
      <button onClick={handleAddItem}>Add Item</button>
      {data && data.map((item) => (
        <div key={item.id}>
          <h2>{item.title}</h2>
          <p>{item.body}</p>
          <button onClick={() => deleteItem(item.id)}>Delete</button>
        </div>
      ))}
    </div>
  );
}

In this example:

  • handleAddItem adds a new item to the store.
  • Each item has a delete button that removes it from the store.

Conclusion

Zustand is a powerful and flexible state management library that simplifies managing state in React applications. With a minimal API, it provides all the tools you need to handle data fetching, storing, and updating state efficiently.

Whether you're building a small project or a large-scale application, Zustand can be a great alternative to more complex state management solutions. Give it a try in your next project!

Subscribe to my newsletter for the latest updates on what I'm building and to explore useful tips and innovations I've come across.