React-Redux-Saga: State management of CRUD operations

silentrobi

Mohammad Abu Musa RABIUL

Posted on September 12, 2021

React-Redux-Saga: State management of CRUD operations

In this section, we will look into state management of CRUD operations using Redux. To manage and access external resources, Redux-Saga will be used.

What is Redux-Saga?

Redux-Saga is a middleware library used to allow a redux store asynchronously to interact with resources outside of itself. This includes making HTTP requests to external services, accessing browser storage, and executing I/O operations.

Firstly, define actions as follow:

import {
    GET_POSTS,
    ADD_POST,
    DELETE_POST,
    UPDATE_POST
} from "./actionTypes";
export const getPosts = () => {
    return {
        type: GET_POSTS,
    };
};
export const addPost = (data) => {
    return {
        type: ADD_POST, payload: data
    };
};
export const updatePost = (data) => {
    return {
        type: UPDATE_POST, payload: data
    };
};
export const deletePost = (id) => {
    return {
        type: DELETE_POST, payload: id
    };
};
Enter fullscreen mode Exit fullscreen mode

Let's define reducer methods. For an example, getPosts reducer is defined as follow:

import * as actionType from "../actions/actionTypes";

const initialState = {
  posts: []
};

export default function (state = initialState, action) {
  switch (action.type) {
    case actionType.GOT_POSTS: {
      return {
        ...state,
        posts: action.payload,
      };
    }
    default: {
      return { ...state };
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, combine all the reducers.

import { combineReducers } from "redux";

import addPost from "./addPost";
import getPosts from "./getPosts";
import updatePost from "./updatePost";
import deletePost from "./deletePost";

export default combineReducers({
    addPost,
    getPosts,
    updatePost,
    deletePost
});

Enter fullscreen mode Exit fullscreen mode

It's time to define our sagas. Let's do it to fetch blog posts.
Create a getPosts saga. It works as follow:

  1. On receiving GET_POSTS action it invokes fetchPosts method. fetchPosts invokes the getAll api service method and pass the response data to another action method GOT_POSTS.
import { put, call, takeEvery } from "redux-saga/effects";
import * as actionType from "../actions/actionTypes";
import postsblogPostApi from "../../server/api";

export default function* getPostsSaga() {
    yield takeEvery(actionType.GET_POSTS, fetchPosts);
}

function* fetchPosts() {
    try {
        const postsResponse = yield call(postsblogPostApi.getAll);
        yield put({ type: actionType.GOT_POSTS, payload: postsResponse });
    } catch (err) {
        console.log(err);
    }
}
Enter fullscreen mode Exit fullscreen mode
  1. Add this saga to redux middleware.
import { createStore, applyMiddleware, compose } from "redux";
import createSagaMiddleware from "redux-saga";
import rootReducer from "./reducers/index";

import { getPostsSaga, addPostSaga, deletePostSaga, updatePostSaga } from "./sagas";

const sagaMiddleware = createSagaMiddleware();

const reduxDevTools =
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__();
const middleware =
    window.__REDUX_DEVTOOLS_EXTENSION__ && process.env.NODE_ENV === "development"
        ? compose(applyMiddleware(sagaMiddleware), reduxDevTools)
        : applyMiddleware(sagaMiddleware);


export const store = createStore(rootReducer, middleware);

sagaMiddleware.run(getPostsSaga);
sagaMiddleware.run(addPostSaga);
sagaMiddleware.run(updatePostSaga);
sagaMiddleware.run(deletePostSaga);
Enter fullscreen mode Exit fullscreen mode
  1. Now, wrap our App component using Provider component and pass store redux object. Let's modify index.js file.
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';

import './index.css';
import App from './App';
import { store } from './redux/store'

ReactDOM.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

Enter fullscreen mode Exit fullscreen mode

Access redux state

Wrap BlogPost component with connect HOC and pass mapStateToProps and mapDispatchToProps.

const mapStateToProps = (state) => {
    return ({
        posts: state.getPosts.posts,
        addedPost: state.addPost.post,
        deletedPost: state.deletePost.post,
        updatedPost: state.updatePost.post
    })
};

const mapDispatchToProps = (dispatch) => ({
    actions: {
        getPosts: () => {
            dispatch(actionType.getPosts());
        },
        addPost: (payload) => {
            dispatch(actionType.addPost(payload));
        },
        deletePost: (payload) => {
            dispatch(actionType.deletePost(payload));
        },
        updatePost: (payload) => {
            dispatch(actionType.updatePost(payload));
        }
    },
});

export default connect(mapStateToProps, mapDispatchToProps)(BlogPost);
Enter fullscreen mode Exit fullscreen mode

We can get redux states from BlogPost component's props object.

const BlogPost = (props) => {
    const { posts, addedPost, deletedPost, updatedPost,  actions } = props;
.......
Enter fullscreen mode Exit fullscreen mode

You can find the project here

Project Demo

💖 💪 🙅 🚩
silentrobi
Mohammad Abu Musa RABIUL

Posted on September 12, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related