# Async Operation in Redux

## Redux Async Data Flow

How data flows in async operation

1. An event is fired (user clicks a button)&#x20;
2. The event handler calls the `dispatch()` function
3. The `dispatch()` function sends the action or thunk (function) to the `reducer()` function&#x20;
4. The middleware intercept the  action or thunk so that the action or thunk can't reach the `reducer()` function directly
5. The middleware does some work (e.g. fetch data from api)
6. Our action dispatches and the reducer returns a new state with updated value&#x20;
7. The new state is passed to the UI which reflects the changes.

<figure><img src="https://109962327-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MC0Wq3Hv_Fc0WRkNF0M%2Fuploads%2F0xbo5bCH299xeC1V4VIN%2FScreenshot%20(519).png?alt=media&#x26;token=7a640675-87ad-456d-a770-55da3f3d86b9" alt=""><figcaption><p>Redux Async Data Flow (Step 1-4)</p></figcaption></figure>

<figure><img src="https://109962327-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MC0Wq3Hv_Fc0WRkNF0M%2Fuploads%2F3dnnIrHNtoWJFbrHVThm%2FScreenshot%20(520).png?alt=media&#x26;token=9d1df403-bb9d-45bc-adc9-0c4723903acf" alt=""><figcaption><p>Redux Async Data Flow (Step 5-6)</p></figcaption></figure>

<figure><img src="https://109962327-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MC0Wq3Hv_Fc0WRkNF0M%2Fuploads%2FJ8zPt83fPeUvpKwLcVnZ%2FScreenshot%20(521).png?alt=media&#x26;token=71491bd6-bffb-473c-910f-b259915793df" alt=""><figcaption><p>Redux Async Data Flow (Step 7)</p></figcaption></figure>

## **Handling Async operation in Redux using Middleware**&#x20;

Redux app that uses middleware to fetch data from api and store them in the state.

{% tabs %}
{% tab title="index.js" %}

```javascript
const { createStore, legacy_createStore, applyMiddleware } = require("redux");
const { delayActionMiddleware, fetchDataMiddleware } = require("./middlewares");

// initial state
const initialState = {
  todos: [],
};

// creating reducers
const todoReducer = (state = initialState, action) => {
  switch (action.type) {
    case "added":
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            title: action.payload,
          },
        ],
      };
    case "loaded":
      return {
        ...state,
        todos: [...state.todos, ...action.payload],
      };

    default:
      break;
  }
};

// creating store
const store = legacy_createStore(
  todoReducer,
  applyMiddleware(delayActionMiddleware, fetchDataMiddleware)
);

// subscription
store.subscribe(() => {
  console.log(store.getState());
});

// dispatch actions
store.dispatch({
  type: "added",
  payload: "Learn NodeJs",
});

store.dispatch({
  type: "fetchData",
});
```

{% endtab %}

{% tab title="middleware.js" %}

```javascript
const fetch = require("node-fetch");

// Define the delayActionMiddleware as a function that takes the Redux store as a parameter
const delayActionMiddleware =
  (store) =>
  // Return another function, which is the actual middleware that Redux will invoke
  (next) =>
  // This function takes the next parameter, which represents the next middleware or the final dispatch function
  (action) => {
    // Check if the action type is "added"
    if (action.type === "added") {
      console.log("Delay....."); // Log a message indicating the delay

      // Set up a timeout using setTimeout to delay the execution of the next(action) call
      setTimeout(() => {
        next(action); // Invoke the next function with the delayed action
      }, 4000);

      return; // Exit the middleware function
    }

    // If the action type is not "added," immediately call the next function with the action
    return next(action);
  };

// Define the delayActionMiddleware as a function that takes the Redux store as a parameter
const fetchDataMiddleware =
  (store) =>
  // Return another function, which is the actual middleware that Redux will invoke
  (next) =>
  // This function takes the next parameter, which represents the next middleware or the final dispatch function
  async (action) => {
    // Check if the action type is "fetchData"
    if (action.type === "fetchData") {
      const res = await fetch(
        "https://jsonplaceholder.typicode.com/todos?_limit=5" // fetch data from api
      );

      const data = await res.json(); // store data

      // dispatch action to reducer
      store.dispatch({
        type: "loaded",
        payload: data,
      });

      console.log(`Number of updated todos: ${store.getState().todos.length}`);

      return; // Exit the middleware function
    }

    // If the action type is not "loaded," immediately call the next function with the action
    return next(action);
  };

module.exports = {
  delayActionMiddleware,
  fetchDataMiddleware,
};

```

{% endtab %}
{% endtabs %}
