Getting started with Redux: Redux-saga with React
We have a series of articles about Getting started with Redux, the links at the end.
Why should we use middleware?
- If we need to communicate with an external API in react, the data fetching can take a while because the promise needs to resolve. Once the promise resolves, you want to dispatch a delayed action to update the state in the Redux store
- the middleware intercepts the action object before Reducer receives it and gives the functionality to perform additional actions.
- That’s where asynchronous action libraries such as Redux Saga, Redux Thunk come into play.
How it works?
[ Action ] <-> [ Middleware ] <-> [ Dispatcher ]
- Middleware is basically a function → that accepts the store → which is expected to return a function → that accepts the next function, and so on.
Now, when creating a new react-redux app, you’ll generally be guided to use redux-thunk or redux-saga to handle asynchronous actions let’s talk about differences between them.
Difference between redux-thunk and redux-saga
- In our story we will talk about redux-saga in details.
What is Saga?
- Saga is a long running process (initialize once, runs for a while).
- waits for events (action).
- responds to actions with new actions, side effects.
Installation:
- install redux-saga
npm install --save redux-saga
2. Connect your sagas to store
import { createStore, applyMiddleware } from 'redux';import createSagaMiddleware from 'redux-saga';import rootReducer from '../reducers';import rootSaga from '../sagas';const saga = createSagaMiddleware();const store = createStore(rootReducer,applyMiddleware(saga));saga.run(rootSaga);export default store;
3. In your API file “./apis/posts.js”
import axios from 'axios';const axiosInstance = axios.create({baseURL: 'http://localhost:3001'});const getPosts = async () => await axiosInstance.get(`/posts`);export default { getPosts };
4. In your typs file “./Types.js”
export const FETCH_POSTS = 'FETCH_POSTS';
export const FETCH_POSTS_REQUEST = 'FETCH_POSTS_REQUEST';
5. In your action file “./action/index.js”
import * as actionTypes from './Types';export const fetchPosts = posts => ({ type: actionTypes.FETCH_POSTS, payload: posts,});export const fetchPostsReq = () => ({
type: actionTypes.FETCH_POSTS_REQUEST });
6. In reducers file “./reducers/posts.js
import * as actionTypes from './Types';export default (state = {}, action) => { switch (action.type) {case actionTypes.FETCH_POSTS: return { ...state, ...action.payload }; default: return state; }
}
- Don’t forget to combine your reducers.
7. In your root sagas file “./sagas/index.js”
import { takeEvery } from 'redux-saga/effects';import * as actionTypes from './Types';import { fetchPostsSaga } from './posts';export function* watchAll() { yield takeEvery(actionTypes.FETCH_POSTS_REQUEST, fetchPostsSaga);}
takeEvery: allows multiple instances of these sagas to run at the same time.
takeLatest: if you dispatch the action before the previous API call finishes, it will stop that call and return only the latest one.
8. In saga file for posts “./sagas/posts.js”
import { put, call } from 'redux-saga/effects';import { fetchPosts } from '../actions';import API from '../apis/posts';export function* fetchPostsSaga() {try { const response = yield call(API.getPosts); yield put(fetchPosts(response.data)); } catch (error) { console.log(error); }}
Sagas effects
Put: it dispatching an action for the reducer to handle the response.
Call: call a ‘API’ function that executes a server call and returns a promise, which will hold the response content when successful.
Generators functions: The function* declaration (function keyword followed by an asterisk) defines a generator function, which returns a Generator object. This method will return the next element in the sequence.
Yield: The keyword yield is used to suspend and resume a generating function.
9. Then all what you need is to call the action in you component.
import React, { Component } from 'react';import { connect } from 'react-redux';import { fetchPostsReq } from './actions';import store from './Store';class PostsList extends Component {componentDidMount() { this.props.fetchPostsReq();} render() { return <h1> Home page <h1> }}const mapStateToProps = state => {return { posts: state.posts};};export default connect(mapStateToProps,{ fetchPostsReq})(PostsList);
Summary
- Redux-saga is a powerful tool that cleans up your code.
- It is allows for complex manipulations of async flow.
- It would be very difficult to accomplish the same things without it, so it is definitely worth the time to learn.
We have a series of articles about Redux:
- Getting started with Redux: what is Redux
- Getting started with Redux: Redux with React
- Getting started with Redux: Redux-saga with React
Thank you for reading, if you have any comments let me know, please :)
That’s all for today see you soon in my next story …👋