Redux-Thunk vs. Redux-Saga

How are thunks different from sagas?

A thunk is a function that already has everything it needs to execute.

Saga , If you are or were a fan of fantasy or science fiction, you already know. For the others, a saga is just a series of connected stories.

Redux-thunk and Redux-saga are both middleware libraries for Redux. Redux middleware is a code that intercepts actions coming into the store via the dispatch() method.

An action can be literally anything.

But if you’re following best practices, an action is a plain javascript object with a type field, and optional payload, meta, and error fields. E.g.

const loginRequest = { 
type: 'LOGIN_REQUEST',
payload: {
name: 'admin',
password: '123',
},
};

This is what the type signature looks like in TypeScript

type Action = {    
type: string;
payload?: any;
meta?: any;
error?: boolean;
};

Redux-Thunk

In addition to dispatching standard actions, Redux-Thunk middleware allows you to dispatch special functions, called thunks.

Thunks (in Redux) generally have the following structure:

export const thunkName =   
parameters =>
(dispatch, getState) => {
// Your application logic goes here
};

That is, a thunk is a function that (optionally) takes some parameters and returns another function. The inner function takes a dispatch function and a getState function -- both of which will be supplied by the Redux-Thunk middleware.

Here we have an example thunk that attempts to call a login API with a username and password. The thunk first dispatches an action indicating that the request is starting. It then makes the call. Finally, it dispatches either a success action or a failure action depending on whether or not the API call succeeded.

import * as api from 'api';
import { loginRequest, loginSuccess, loginFailure } from './loginActions';
export const loginThunk =
(name: string, password: string) =>
(dispatch: Function) => {
dispatch(loginRequest());
try {
api.login(name, password);
}
catch (err) {
dispatch(loginFailure(err));
return;
}
dispatch(loginSuccess());
};

When you dispatch your thunk, e.g. dispatch(loginThunk('admin', 'secret')); Redux-Thunk calls your inner function, which is essentially:

(dispatch: Function) => {
dispatch(loginRequest());
try {
api.login('admin', 'secret'); // values from closure
}
catch (err) {
dispatch(loginFailure(err));
return;
}
dispatch(loginSuccess());
};

Now that we’ve covered the basics of Redux-thunk, let’s look at Redux-Saga

Redux-Saga

Redux-Saga middleware allows you to express complex application logic as pure functions called sagas. Pure functions are desirable from a testing standpoint because they are predictable and repeatable, which makes them relatively easy to test.

Sagas are implemented through special functions called generator functions. These are a new feature of ES6 JavaScript. Basically, execution jumps in and out of a generator everywhere you see a yield statement. Think of a yield statement as causing the generator to pause and return the yielded value. Later on, the caller can resume the generator at the statement following the yield.

A generator function is one defined like this. Notice the asterisk after the function keyword.

function* mySaga() {    // ...}

We can rewrite the login functionality from before as a saga. It looks like this:

import * as api from 'api';
import { LoginRequestAction, loginSuccess, loginFailure } from './loginActions';
function* loginSaga() {
const action: LoginRequestAction = yield take('LOGIN_REQUEST'); const { name, password } = action.payload;
try {
yield call(api.login, name, password);
}
catch (err) {
yield put(loginFailure(err));
return;
}
yield put(loginSuccess());
}

Once the login saga is registered with Redux-Saga, it will begin executing immediately. But then the yield takeon the the first line will pause the saga until an action with type 'LOGIN_REQUEST' is dispatched to the store. Once that happens, execution will continue.