Setting Up Redux with Redux Thunk and Persist in Next.js
Managing state efficiently in a Next.js application is crucial, especially when handling user authentication. In this guide, we'll walk you through setting up Redux with Redux Thunk and Redux Persist to manage authentication seamlessly and ensure state persistence across sessions.
Project Structure
Understanding the folder structure is essential for organized development. Here’s the project layout:
project-root/
│── src/
│ ├── pages/
│ │ ├── login.js
│ ├── store/
│ │ ├── actions/
│ │ │ ├── auth.js
│ │ ├── reducers/
│ │ │ ├── auth.js
│ │ ├── store.js
│ │ ├── init.js
│ ├── services/
│ │ ├── auth.services.js
│ ├── config/
│ │ ├── constants.js
│ │ ├── validationSchema.js
1. Installing Dependencies
To get started, install the required dependencies:
npm install redux react-redux redux-thunk redux-persist
2. Configuring the Redux Store
Create a store.js file inside the
src/store directory to set up Redux and state
persistence:
import { persistReducer, persistStore } from "redux-persist";
import storage from "redux-persist/lib/storage";
import rootReducer from "./reducers";
import { applyMiddleware, createStore } from "redux";
import thunk from "redux-thunk";
const persistConfig = {
key: "root",
storage,
};
const persistedReducer = persistReducer(persistConfig, rootReducer);
const store = createStore(persistedReducer, applyMiddleware(thunk));
const persistor = persistStore(store);
export { store, persistor };
Key Takeaways:
-
persistReducer&persistStore: Ensure Redux state persists after page reloads. -
applyMiddleware(thunk): Enables async actions like API calls. -
Exports
storeandpersistor: These will be used inProviderandPersistGatein_app.js.
3. Initializing Default State
Create an init.js file inside src/store/ to
define the default authentication state:
export const AUTH = {
isAuthenticated: false,
token: null,
};
4. Creating the Authentication Reducer
Inside src/store/reducers/auth.js, define the authentication
reducer:
import { AUTH } from "../init";
const auth = (state = AUTH, action) => {
switch (action.type) {
case "SET_TOKEN":
return { ...state, isAuthenticated: true, token: action.payload };
case "LOGOUT":
return AUTH;
default:
return state;
}
};
export { auth };
Key Takeaways:
-
SET_TOKEN: Updates state with the user's authentication token. -
LOGOUT: Clears the authentication state. -
AUTH: Default state imported frominit.js.
5. Defining Authentication Actions
In src/store/actions/auth.js, define login and logout
actions:
import { toast } from "sonner";
import { login } from "../../services/auth.services";
export const userLogin = (payload) => async (dispatch) => {
try {
const response = await login(payload);
if (response.status === 200) {
dispatch({
type: "SET_TOKEN",
payload: response.data.token,
});
}
} catch (error) {
if (error.response) {
toast.error(error.response.data.message);
}
}
};
Key Takeaways:
-
userLogin: Dispatches API response data to Redux. -
Error Handling: Displays error messages using
toast.error(). -
Uses Redux Thunk: Handles async operations efficiently.
6. Implementing the Authentication Service
Inside src/services/auth.services.js, create the login API
call:
import { MODULES } from "../config/constants";
import { http } from "./http";
export const login = async (payload) => {
try {
return await http.post(MODULES.AUTH.LOGIN, payload);
} catch (error) {
throw error;
}
};
Key Takeaways:
-
Encapsulates API Calls: Centralized authentication logic.
-
Handles API Errors: Throws errors for Redux action handling.
-
Uses
httpModule: Helps maintain cleaner service calls.
7. Building the Login Page
In src/pages/login.js, integrate Redux authentication:
import React from "react";
import { Formik } from "formik";
import { login } from "../config/validationSchema";
import { userLogin } from "../store/actions/auth";
import { connect } from "react-redux";
const Login = (props) => {
return (
<div className="bg-base">
<div className="container h-[100vh]">
<Formik
initialValues={{ email: "", password: "" }}
validationSchema={login}
onSubmit={(values) => props.userLogin(values)}
>
{({ handleChange, handleSubmit }) => (
<form onSubmit={handleSubmit}>
<input
type="email"
name="email"
onChange={handleChange}
placeholder="Email"
/>
<input
type="password"
name="password"
onChange={handleChange}
placeholder="Password"
/>
<button type="submit">Login</button>
</form>
)}
</Formik>
</div>
</div>
);
};
export default connect(null, { userLogin })(Login);
Key Takeaways:
-
Formik for Forms: Handles validation and submission efficiently.
-
Redux Integration: Dispatches
userLogin()action. -
Connects to Redux Store: Uses
connect()to pass actions as props.
.png)
Comments
Post a Comment