Blog>
Snippets

Using Redux-Saga for Type-Safe Asynchronous Flows

Set up a Redux-Saga example where type checking is enforced in complex asynchronous flows to prevent type issues when dispatching actions after async operations.
import { createStore, applyMiddleware } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { call, put, takeEvery } from 'redux-saga/effects';
import { ActionType, TypedActionCreator } from 'typesafe-actions';

// Define action types
enum ActionTypes {
  FETCH_START = 'FETCH_START',
  FETCH_SUCCESS = 'FETCH_SUCCESS',
  FETCH_FAILURE = 'FETCH_FAILURE'
}

// Define action creators with typesafe-actions
const fetchStart: TypedActionCreator<ActionTypes.FETCH_START> = () => ({
  type: ActionTypes.FETCH_START,
});

const fetchSuccess: TypedActionCreator<ActionTypes.FETCH_SUCCESS> = (data: any) => ({
  type: ActionTypes.FETCH_SUCCESS,
  payload: data,
});

const fetchFailure: TypedActionCreator<ActionTypes.FETCH_FAILURE> = (message: string) => ({
  type: ActionTypes.FETCH_FAILURE,
  payload: message,
});

// Define the root reducer
const rootReducer = (state = {}, action: ActionType<typeof fetchStart | typeof fetchSuccess | typeof fetchFailure>) => {
  switch (action.type) {
    case ActionTypes.FETCH_SUCCESS:
      return { ...state, data: action.payload };
    case ActionTypes.FETCH_FAILURE:
      return { ...state, error: action.payload };
    default:
      return state;
  }
};

// Define a saga to handle fetching
function* fetchSaga(): Generator {
  try {
    const data = yield call(apiFetch); // Replace 'apiFetch' with your API call
    yield put(fetchSuccess(data));
  } catch (error) {
    yield put(fetchFailure(error.message));
  }
}

// Root saga
function* rootSaga() {
  yield takeEvery(ActionTypes.FETCH_START, fetchSaga);
}

// Setup saga middleware
const sagaMiddleware = createSagaMiddleware();

// Create store with saga middleware
const store = createStore(
  rootReducer,
  applyMiddleware(sagaMiddleware)
);

// Run the root saga
sagaMiddleware.run(rootSaga);

export { store, fetchStart, fetchSuccess, fetchFailure };
// This code sets up a redux store with saga middleware, using type-safe actions to fetch data asynchronously. The saga handles fetch side effects, capturing both success and failure cases.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Redux-Saga Type-Safe Example</title>
    <script src="/path/to/your/compiled-redux-saga-code.js"></script>
</head>
<body>
    <div id="app">
      <!-- Content will be managed by React/Redux etc. -->
    </div>
</body>
</html>
// This is the HTML structure where Redux-Saga will be used. The actual Redux-Saga logic would be in a separate JavaScript file included with a script tag.
body {
    font-family: 'Arial', sans-serif;
}

#app {
    margin: 20px;
    padding: 20px;
    border: 1px solid #ccc;
}
// Basic CSS for the #app to make it visually distinct with some padding and a border.