Blog>
Snippets

Creating a Type-Safe Async Action Flow

Outline how to create a type-safe async action flow, using Redux Thunk or Redux Saga with Redux v5.0.0 to handle complex asynchronous operations.
import { createAction, createReducer, configureStore } from '@reduxjs/toolkit';
import thunkMiddleware from 'redux-thunk';

// Define the state type
interface AsyncState {
  loading: boolean;
  data?: any;
  error?: string;
}

// Define action types
enum AsyncActionTypes {
  FETCH_START = 'async/fetchStart',
  FETCH_SUCCESS = 'async/fetchSuccess',
  FETCH_FAILURE = 'async/fetchFailure',
}

// Action creators using createAction from Redux Toolkit for type safety
const fetchStart = createAction(AsyncActionTypes.FETCH_START);
const fetchSuccess = createAction<any>(AsyncActionTypes.FETCH_SUCCESS);
const fetchFailure = createAction<string>(AsyncActionTypes.FETCH_FAILURE);
This code sets up the initial Redux state, action types, and action creators using Redux Toolkit, which ensures type safety for the actions. Typed actions help manage the async flow.
// Async thunk action using redux-thunk, performs the async operation
const fetchSomeData = () => (
  dispatch,
  getState
) => {
  dispatch(fetchStart());
  fetch('/some-api-endpoint')
    .then(response => response.json())
    .then(data => dispatch(fetchSuccess(data)))
    .catch(error => dispatch(fetchFailure(error.message)));
};
This is the async thunk action created using redux-thunk. It dispatches fetchStart before making an API call. Upon success, it dispatches fetchSuccess with the data or dispatches fetchFailure with an error.
// Reducer using createReducer from Redux Toolkit for type safety
const asyncReducer = createReducer<AsyncState>({ loading: false }, {
  [fetchStart.type]: state => ({...state, loading: true}),
  [fetchSuccess.type]: (state, action) => ({loading: false, data: action.payload}),
  [fetchFailure.type]: (state, action) => ({loading: false, error: action.payload}),
});
Creates a reducer with the initial state and handlers for each action type. The state is typed as AsyncState for type safety, and the reducer modifies state according to each action.
// Configure the store with the reducer and thunk middleware
const store = configureStore({
  reducer: asyncReducer,
  middleware: [thunkMiddleware],
});
This creates a Redux store, configured with the asyncReducer and redux-thunk middleware. This setup allows the dispatching of thunk actions for asynchronous operations.