Back to roadmaps zustand Course

Asynchronous Actions and Data Fetching

Unlike Redux, which requires middleware helpers (like Redux Thunk or Saga) to process async operations, Zustand allows you to write asynchronous actions directly in your store.


1. Defining Asynchronous Actions

Let us build a user directories store that fetches details from a remote API. The store manages three states:

  • data (The returned payload array).
  • isLoading (A boolean indicating if the request is loading).
  • error (Contains any connection error message strings).
// src/stores/useUsersStore.ts
import { create } from "zustand";

interface User {
  id: number;
  name: string;
  email: string;
}

interface UsersState {
  users: User[];
  isLoading: boolean;
  error: string | null;
  fetchUsers: () => Promise<void>;
}

export const useUsersStore = create<UsersState>((set) => ({
  users: [],
  isLoading: false,
  error: null,

  // Async action
  fetchUsers: async () => {
    // Set loading indicator
    set({ isLoading: true, error: null });
    
    try {
      const response = await fetch("https://jsonplaceholder.typicode.com/users");
      
      if (!response.ok) {
        throw new Error("Network response failed");
      }
      
      const data = await response.json();
      
      // Save data to store
      set({ users: data, isLoading: false });
    } catch (err: any) {
      // Catch and log error
      set({ error: err.message || "Unknown error", isLoading: false });
    }
  },
}));

2. Consuming Async State in React Components

Once defined, you can read the loading state, show error warnings, or render the data grid, and trigger the fetch when the component mounts:

// src/components/UsersList.tsx
import { useEffect } from "react";
import { useUsersStore } from "../stores/useUsersStore";

export default function UsersList() {
  const { users, isLoading, error, fetchUsers } = useUsersStore();

  // Trigger fetch users when component loads
  useEffect(() => {
    fetchUsers();
  }, [fetchUsers]);

  if (isLoading) return <p>Loading users profiles...</p>;
  if (error) return <p className="text-red-500">Error: {error}</p>;

  return (
    <ul className="divide-y">
      {users.map((user) => (
        <li key={user.id} className="py-3">
          <p className="font-bold">{user.name}</p>
          <p className="text-sm text-gray-500">{user.email}</p>
        </li>
      ))}
    </ul>
  );
}
Published on Last updated: