# Async Actions and the Slices Pattern — Redux Toolkit / Zustand

Source: https://www.skillbyai.com/en/redux-zustand/z-async

> Fetch in actions; split big stores.

## Actions can be async

Zustand has no special async API: an action is a function, so it can be `async`, call `set` before and after `await`, and read fresh state with `get()`. Track loading and error fields next to the data. For large stores, the **slices pattern** splits state into creator functions typed with `StateCreator<Combined, [], [], Slice>` and spreads them into one `create` call, so each feature owns its part while actions can still reach the whole store. For server data, consider pairing Zustand with TanStack Query rather than hand-rolling caching.

## An async slice combined with another

Slices with StateCreator.

```typescript
import { create, type StateCreator } from 'zustand';

type UserSlice = {
  user: User | null;
  status: 'idle' | 'loading' | 'error';
  loadUser: (id: string) => Promise<void>;
};
type UiSlice = { sidebarOpen: boolean; toggleSidebar: () => void };
type Store = UserSlice & UiSlice;

const createUserSlice: StateCreator<Store, [], [], UserSlice> = (set) => ({
  user: null,
  status: 'idle',
  loadUser: async (id) => {
    set({ status: 'loading' });
    try {
      const res = await fetch(`/api/users/${id}`);
      if (!res.ok) throw new Error(String(res.status));
      set({ user: await res.json(), status: 'idle' });
    } catch {
      set({ status: 'error' });
    }
  },
});

const createUiSlice: StateCreator<Store, [], [], UiSlice> = (set) => ({
  sidebarOpen: false,
  toggleSidebar: () => set((s) => ({ sidebarOpen: !s.sidebarOpen })),
});

export const useStore = create<Store>()((...a) => ({
  ...createUserSlice(...a),
  ...createUiSlice(...a),
}));
```

## Guard against stale responses

If the same action can run twice (fast typing, quick navigation), ignore late responses, for example by checking that the requested id still matches before calling set, or by using an AbortController.

**Quiz:** How do you write an async action in Zustand?

- [x] As an async function in the store that calls set before and after await
- [ ] With a required createAsyncThunk import
- [ ] Only through middleware
- [ ] By returning a Promise from the selector

*Answer:* As an async function in the store that calls set before and after await. Actions are plain functions.
