How to Implement Redux Saga in React
This article provides a straightforward, step-by-step guide on how to integrate and use Redux Saga in a React application. You will learn how to install the necessary packages, configure the Redux store with the saga middleware, write generator functions to handle asynchronous side effects, and dispatch actions from your React components to trigger these sagas.
Step 1: Install the Required Packages
To get started, you need to install Redux Toolkit (the recommended way to write Redux logic), React-Redux, and Redux Saga. Run the following command in your terminal:
npm install @reduxjs/toolkit react-redux redux-sagaStep 2: Create the Redux Slice
Create a Redux slice to manage the state and reducers. For this
example, we will fetch user data from an API. Create a file named
userSlice.js:
import { createSlice } from '@reduxjs/toolkit';
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null,
},
reducers: {
fetchUserRequest: (state) => {
state.loading = true;
state.error = null;
},
fetchUserSuccess: (state, action) => {
state.loading = false;
state.data = action.payload;
},
fetchUserFailure: (state, action) => {
state.loading = false;
state.error = action.payload;
},
},
});
export const { fetchUserRequest, fetchUserSuccess, fetchUserFailure } = userSlice.actions;
export default userSlice.reducer;Step 3: Create the Saga
Sagas use ES6 generator functions to handle side effects. Create a
file named userSaga.js to define the worker saga (which
performs the API call) and the watcher saga (which listens for
dispatched actions).
import { call, put, takeLatest } from 'redux-saga/effects';
import { fetchUserRequest, fetchUserSuccess, fetchUserFailure } from './userSlice';
// Mock API function
const fetchUserDataApi = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users/1');
if (!response.ok) {
throw new Error('Failed to fetch user');
}
return response.json();
};
// Worker Saga
function* workFetchUser() {
try {
const userData = yield call(fetchUserDataApi);
yield put(fetchUserSuccess(userData));
} catch (error) {
yield put(fetchUserFailure(error.message));
}
}
// Watcher Saga
function* userSaga() {
yield takeLatest(fetchUserRequest.type, workFetchUser);
}
export default userSaga;Step 4: Configure the Store with Saga Middleware
Next, create the Redux store, instantiate the saga middleware, and
run your watcher saga. Create a file named store.js:
import { configureStore } from '@reduxjs/toolkit';
import createSagaMiddleware from 'redux-saga';
import userReducer from './userSlice';
import userSaga from './userSaga';
const sagaMiddleware = createSagaMiddleware();
const store = configureStore({
reducer: {
user: userReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ thunk: false }).concat(sagaMiddleware),
});
sagaMiddleware.run(userSaga);
export default store;Step 5: Provide the Store to Your React App
Wrap your root React component with the Provider from
react-redux and pass the configured store as a prop. Modify
your main.js or index.js file:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);Step 6: Dispatch the Action from a Component
Finally, use the useDispatch hook to trigger the saga
and the useSelector hook to read the state inside a React
component. Create or update your App.jsx file:
import React from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { fetchUserRequest } from './userSlice';
function App() {
const dispatch = useDispatch();
const { data, loading, error } = useSelector((state) => state.user);
return (
<div style={{ padding: '20px' }}>
<h1>Redux Saga Integration</h1>
<button onClick={() => dispatch(fetchUserRequest())} disabled={loading}>
{loading ? 'Loading...' : 'Fetch User Data'}
</button>
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
{data && (
<div style={{ marginTop: '20px' }}>
<h3>User Details:</h3>
<p><strong>Name:</strong> {data.name}</p>
<p><strong>Email:</strong> {data.email}</p>
<p><strong>Website:</strong> {data.website}</p>
</div>
)}
</div>
);
}
export default App;