Front end applications are often written today in React because of the large community, ecosystem, available resources, and flexibility.

In most of these cases, state management of these applications is done with Redux, and depending on the complexity of the application you end up needing to choose a middleware to manage the side effects of your application.

What is Redux?

Redux is a predictable state container for JavaScript applications.

It helps to write applications that behave consistently, run in different environments (client, server and native) and are easy to test. It also provides a great developer experience, such as live code editing combined with a time-traveling debugger.

What are sagas?

Redux Saga is a library that aims to make application side effects (ie asynchronous things like data fetching and filthy things like accessing the browser cache) easier to manage, more efficient to execute, easier to test and better in fault handling.

The mental model is that a saga is like a separate thread in your application, which is solely responsible for the side effects. redux-saga is a redux middleware, which means that this thread can be started, paused and canceled from the main application with normal redux actions, it has access to the full state of the redux application and can also send redux actions.

It uses an ES6 feature called Generators to make it easy to read, write, and test these asynchronous streams. In doing so, these asynchronous streams look like standard synchronous JavaScript code. (like async/await, but generators have some more awesome features we need)

The term saga was first used in 1987 and is not unique to the JavaScript or Redux world. This pattern is widely used in microservices architectures to improve consistency in the transition of information between services.

What justifies the use of these technologies?

Redux is the industry's most established architecture in terms of centralized React application state management. But using only Redux we get a second problem when our application grows too big (or starts big).

The big problem here comes when managing the side effects of our application (asynchronous requests, parallel processing, queuing, canceling actions etc). Today the most famous solutions for managing these side effects in Redux are thunks and sagas.

Redux thunk

import { Types } from '../auth'

export function authSuccess(token) {
  return {
    type: Types.AUTH_SUCCESS,
    payload: token,
  }
}

export function authFailure(error) {
  return {
    type: Types.AUTH_FAILURE,
    payload: error,
  }
}

export function auth(credentials) {
  return dispatch => {
    fetch('/login', { method: 'POST', body: credentials })
      .then(res => res.json())
      .then(res => dispatch(authSuccess(res)))
      .catch(err => dispatch(authFailure(err)))
  }
}

In most cases on smaller projects the use of Thunk is more than enough if you need to make very few asynchronous requests and your application is not complex.

Redux Thunk basically extends the capabilities of your redux store and allows you to do more complex synchronous operations as well as simple asynchronous operations like AJAX requests.

PROS

  • Uses promises and can use async / await
  • Simplicity and ease

CONS

  • Callback hell
  • Complexity to climb
  • Complexity for writing tests (needs mocks)
  • Greater difficulty performing more complex asynchronous operations (race condition, parallelism, etc.)
  • Redux saga

Thinking big? Think Saga!

import { takeLatest, call, put } from 'redux-safa/effects'
import { Types, authSuccess, authFailure } from '../auth'
import * as api from '../api'

function* authenticate(credentials) {
  try {
    const token = yield call(api.login, credentials)   
    yield put(authSuccess(token))
  } catch(err) {
    yield put(authFailure(err))
  }
}

export default function* () {
  yield takeLatest(Types.AUTH, authenticate)
}

If your application is already designed for medium or large scale, or if you have ambitious plans to scale your application, saga is the right choice.

Saga is also a middleware that has full access to its redux store, but the difference is that it keeps watching all the dispatched actions and deciding what to do with them, using Generators functions, which is a new feature of ES6.

PROS

  • Easy scale
  • No action creators required
  • The code is written synchronously
  • Wheel in the background
  • Easy to test

CONS

  • Need to learn Generators
  • Adds greater complexity to managing events.

With all that said, we fall into that old dilemma that there is no silver bullet.