Redux Saga allows you to work with Next.js in the form of a solution for side effects such as API calls, background tasks, and complex async flows. Below is a guide on how to implement Redux Saga in a Next.js project (App Router).
Install Redux, Saga, and React-Redux for state management and async.
npm install @reduxjs/toolkit redux redux-saga react-redux
Set up the Redux store along with the Saga Middleware.
import { configureStore } from "@reduxjs/toolkit";
import createSagaMiddleware from "redux-saga";
import { rootSaga } from "./sagas";
import rootReducer from "./reducers";
const sagaMiddleware = createSagaMiddleware();
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({ thunk: false }).concat(sagaMiddleware),
});
sagaMiddleware.run(rootSaga);
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
Create Redux state and actions; create actions that kick off async requests.
import { createSlice } from "@reduxjs/toolkit";
const counterSlice = createSlice({
name: "counter",
initialState: { value: 0 },
reducers: {
increment: (state) => { state.value += 1; },
decrement: (state) => { state.value -= 1; },
incrementAsync: () => {}, // Action handled by Saga
},
});
export const { increment, decrement, incrementAsync } = counterSlice.actions;
export default counterSlice.reducer;
Listen for the incrementAsync action and perform the effect: invoke the API.
import { takeEvery, put, delay } from "redux-saga/effects";
import { increment } from "../reducers/counterReducer";
function* handleIncrementAsync() {
yield delay(1000); // Simulate API delay
yield put(increment()); // Dispatch actual increment action
}
export function* watchIncrementAsync() {
yield takeEvery("counter/incrementAsync", handleIncrementAsync);
}
Combine all the sagas to a root saga.
import { all } from "redux-saga/effects";
import { watchIncrementAsync } from "./counterSaga";
export function* rootSaga() {
yield all([watchIncrementAsync()]);
}
Wrap the app with the Redux Provider so that any component can connect with and access any part of the provided store.
"use client";
import { Provider } from "react-redux";
import { store } from "../redux/store";
export default function Providers({ children }: { children: React.ReactNode }) {
return <Provider store={store}>{children}</Provider>;
}
As a React component, it manages the connection and dispatches actions.
"use client";
import { useSelector, useDispatch } from "react-redux";
import { RootState } from "../redux/store";
import { increment, decrement, incrementAsync } from "../redux/reducers/counterReducer";
export default function Counter() {
const count = useSelector((state: RootState) => state.counter.value);
const dispatch = useDispatch();
return (
<div>
<h1>Count: {count}</h1>
<button onClick={() => dispatch(increment())}>Increment</button>
<button onClick={() => dispatch(decrement())}>Decrement</button>
<button onClick={() => dispatch(incrementAsync())}>Increment After 1s</button>
</div>
);
}
The saga will handle async with API calls and delays. Sagas listen for specific actions, such as increment Async. Side effects are similar to delay or invoke an API call, and a new action is dispatched from this code. The integration is focused on App Router for Next.js.
Ready to transform your business with our technology solutions? Contact Us today to Leverage Our ReactJS Expertise.