Engineering

Elevate Your React Projects with Redux Saga and Toolkit Integration

-By Priya Nair

Struggling with messy code in your web projects? Redux Saga and Redux Toolkit are your go-to allies in the realm of web development.

Redux Saga: A middleware that handles side effects, such as async tasks and data fetching, in Redux. By leveraging ES6 Generators, its amplifies asynchronous coding, making it more readable and testable. It's designed to compartmentalize asynchronous operations, ensuring they're tidily kept within distinct saga functions, leading to a decluttered codebase and improved maintainability.

Redux Toolkit: An official Redux package that revolutionizes store setup, reducer creation, and action handling. With an emphasis on best practices, it reduces boilerplate and streamlines state management for efficiency and intuitiveness.

Merging these tools promises a coding environment that is organized, less error-prone, and perfectly poised for scalability. Step in, and let's transform the way you perceive and manage state and asynchronous actions in your projects.

 

Why choose Redux Saga?

Getting Started with Redux Saga and Redux Toolkit: A Practical Guide

Let’s start by initiating a new React project, and then weave in Redux Saga and Redux Toolkit.

Step1: Initiate a React Project

Fire up a new React project with this command:

npx create-react-app redux-saga-project

Step2: Add Necessary Libraries

Move to the project's directory and bring in the essential libraries:

npm install react-redux @reduxjs/toolkit redux-saga

Step3: Structure Your Project

Forge a redux directory at the project's base. It's the sanctuary for all Redux-centric code. Within redux, spawn two offspring folders: saga (for saga-related code) and features (for reducer slices).

In the features Folder:

Create a reducer slice for managing user data state in a file named userSlice.js:

import { createSlice } from'@reduxjs/toolkit';

 

const initialState = {

 user:{

   id:'',

  username: '',

 },

 error:'',

};

 

export const userSlice = createSlice({

 name: 'userData',

initialState,

reducers: {

  fetchDataSuccess: (state, action) => {

    state.user = action.payload;

   },

  fetchDataError: (state, action) => {

    state.error = action.payload;

   },

  resetTemporaryState: (state) => {

    return initialState; // Return a new state object instead of modifyingthe existing one

   },

 },

});

 

export const { fetchDataSuccess,fetchDataError, resetTemporaryState } = userSlice.actions;

 

export const userDataSelector = (state) =>state.userData.user;

 

export default userSlice.reducer;

 

 

The slice needs parameters like name,initialState and list of reducers. Here the reducers we are using is forsuccessful fetch, data error and to reset state.

In the saga Folder:

Create a saga for fetching user data in a file named userSaga.js:

The main concept used in Redux Saga is Generators, fetch UserData is a generator function to fetch api calls. 


 

 

import { take Latest, call, put } from 'redux-saga/effects';

import { GET_USERS, fetchDataError, fetchDataSuccess } from '../features/userSlice';

 

function* fetchUserData(val) {

 try {

  const id = val.payload.id;

  const response = yield call(fetch, `https://dummyjson.com/users/${id}`);

  const data = yield response.json();

  yield put(fetchDataSuccess({ id: data.id, username: data.firstName }));

 }catch (error) {

  yield put(fetchDataError(error));

 }

}

 

export function* watchUserData() {

 yield takeLatest(GET_USERS, fetchUserData);

//fetchUserData is called when user dispatches an action “GET_USERS”

}

 

In this code the fetch api iscalled and response of api call is stored in the user state , similar case forerror.

Configure Redux Store in index.js:

Inside the redux folder we’ll create a index.jsfile. This is just a boilerplate code that we need to configure once and herewe are gonna pass reducers and slices we made

 

import createSagaMiddleware from"@redux-saga/core";

import { configureStore } from"@reduxjs/toolkit";

import { all } from 'redux-saga/effects';

import { watchUserData } from"./sagas/userSaga";

import userReducer from"./features/userSlice";

 

function* rootSaga() {

 yieldall([watchUserData()]);

}

 

const sagaMiddleware = createSagaMiddleware();

 

const store = configureStore({

reducer: {

  userData: userReducer,

 },

middleware: [sagaMiddleware],

});

 

sagaMiddleware.run(rootSaga);

 

export default store;

Connect to the Application:

Wrap your app with the Redux Providercomponent in the index.js file:

 

import React from 'react';

import ReactDOM from 'react-dom';

import { Provider } from 'react-redux';

import store from './redux/index';

import App from './App';

import './index.css';

 

ReactDOM.render(

<Provider store={store}>

  <React.StrictMode>

    <App />

  </React.StrictMode>

</Provider>,

document.getElementById('root')

);

Final Steps - Using Redux Saga and Redux Toolkit:

In your App.js file, connect the component toRedux and utilize the actions and state:

 

import React from 'react';

import { useDispatch, useSelector } from'react-redux';

import { GET_USERS, resetTemporaryState } from'./redux/features/userSlice';

 

function App() {

 constdispatch = useDispatch();

 constuserState = useSelector((state) => state.userData.user);

// To access the state data weneed to use the hook useSelector, userData is exported from the userslice

 

 

 constcallApi = () => {

// dispatch the type correspondingto the generator function you need to call, along with the payload you want tosend. Payload is optional

  dispatch({

    type: GET_USERS,

    payload: { id: 1 },

   });

 };

 

 constclearData = () => {

  dispatch(resetTemporaryState()); // This is how you call the reducersdirectly

  // dispatch(fetchDataSuccess({id:2,name:"Priya"})) // This isjust an example on how to save the data in state

 

 };

 

 return(

  <div className="App">

    <button onClick={callApi}>Fetch</button>

    <button onClick={clearData}>Clear</button>

    <pre>{JSON.stringify(userState, null, 2)}</pre>

  </div>

 );

}

 

export default App;

 

Setting up Redux Saga with Redux Toolkit in a React app? This guide breaks it down. Learn to create reducer slices, define sagas, configure the Redux store, and link components using useSelector and useDispatch hooks.

While this provides a foundational understanding, real-world applications may involve more advanced scenarios and error handling. Use this tutorial to master the asynchronous actions and state management capabilities of Redux Saga and Redux Toolkit.

Conclusion

Navigating the decision to incorporate Redux Saga into your toolkit requires a thoughtful approach. This powerful middleware, expert at handling intricate asynchronous workflows and coordinating multiple actions, also comes equipped with built-in methods for cancellation, debouncing, and error handling. Yet, it brings its own set of challenges, including a steep learning curve and increased code complexity. While invaluable for complex scenarios, other Redux middleware or hooks might be more suitable for simpler projects. Ultimately, the specificity of your application's needs should guide your decision. A deep dive into ReduxSaga's strengths and limitations is essential for making a choice that will enhance your Redux application's maintainability and efficiency. Choose wisely, and watch your application thrive!

You may also like