MagoDocs
Guides

Async Actions

Handling Async Actions & APIs

Async Actions

In modern web apps, data doesn't just sit there—it flows from APIs. Magos makes handling asynchronous logic (like fetch or axios) feel like writing plain JavaScript. No middlewares, no complex patterns.

1. The Async Action Pattern

Since the actionFactory gives you access to the set function, you can call it multiple times within a single async function. This is perfect for managing Loading, Success, and Error states.

The Scenario: Fetching a User Profile

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

  interface UserState {
    data: UserData | null;
    isLoading: boolean;
    error: string | null;
  }

  const initialState: UserState = {
    data: null,
    isLoading: false,
    error: null
  };

  const userBox = createBox(initialState, (set) => ({
    fetchUser: async (userId) => {
      // Step 1: Set loading to true to show a spinner in the UI
      set(prev => ({ ...prev, isLoading: true, error: null }));

      try {
        const response = await fetch(`https://api.example.com/users/${userId}`);
        
        if (!response.ok) throw new Error("User not found");

        const data = await response.json();

        // Step 2: Success! Update data and turn off loading
        set({ data, isLoading: false, error: null });

      } catch (err) {
        // Step 3: Handle errors gracefully
        set(prev => ({ ...prev, isLoading: false, error: err.message }));
      }
    }
  })
);
const initialState = {
  data: null,
  isLoading: false,
  error: null
};

const userBox = createBox(initialState, (set) => ({
  fetchUser: async (userId) => {
    // Step 1: Set loading to true to show a spinner in the UI
    set(prev => ({ ...prev, isLoading: true, error: null }));

    try {
      const response = await fetch(`https://api.example.com/users/${userId}`);
      
      if (!response.ok) throw new Error("User not found");

      const data = await response.json();

      // Step 2: Success! Update data and turn off loading
      set({ data, isLoading: false, error: null });

    } catch (err) {
      // Step 3: Handle errors gracefully
      set(prev => ({ ...prev, isLoading: false, error: err.message }));
    }
  }
})
);

2. How it works

The beauty of Magos lies in the set function's availability within the actionFactory. Since actions are standard JavaScript functions, they can be async by nature.

  • Sequential Updates: You can update the state multiple times within a single action. For instance, updating a loading flag before and after an await call.
  • Encapsulated Logic: All the logic—from triggering the request to parsing the JSON and handling errors—is contained within the Box. Your UI components stay "clean" and only care about the final state.
  • Native Performance: Magos processes each set call immediately. When your API returns data and you call set, Magos instantly notifies all subscribers to update the UI.

On this page