Explaining Redux Toolkit and Authentication in React: A Code Breakdown

in blurt •  last year  (edited)

Introduction:

In this blog post, we will dissect the provided code snippet that utilizes Redux Toolkit and handles authentication in a React application. We'll explain the purpose of each section and explore how it contributes to managing user authentication, fetching API data, and maintaining application state.

src

Understanding the Code:

Importing Dependencies:

The code begins by importing necessary dependencies like createAsyncThunk and createSlice from the @reduxjs/toolkit package. It also imports Cookies for managing browser cookies, and ApiCallHeaderAndBody for constructing API headers and request bodies.

Initial State:

The initialState object defines the initial values for various properties related to authentication, such as sidebarFixed, logout, userEmail, loadTokenVerify, token, tokenError, permissions, currentUserInitialCollege, and currentUserCollegesList. These properties hold different aspects of the user's authentication status and data.

Async Thunks:

The code defines three asynchronous thunks using createAsyncThunk. Thunks are functions that can be dispatched to perform asynchronous actions. These thunks handle fetching user permissions, logging out the user, and fetching token information from the API.

fetchPermission:

This thunk fetches user permissions based on the provided token and college ID. It makes an HTTP GET request to the API endpoint and stores the response in the Redux state.

fetchLogout:

This thunk initiates the logout process by making an HTTP GET request to the logout API endpoint. It removes the user's authentication data from the Redux state and updates the logout property to indicate successful logout.

fetchToken:

This thunk fetches token information from the API. It sends a POST request to the token info endpoint with the provided token and stores the response in the Redux state.

Auth Slice:

The authSlice is created using createSlice. It defines the name of the slice (authSlice), the initial state (initialState), and the reducers.

Reducers:

The reducers section contains action creators to update specific properties in the state. These reducers include setUserEmail, removeCookies, setLoadTokenVerify, setSidebarFixed, setPermissions, setTokenInfo, and setUserCollegeInfo. Each reducer modifies the corresponding state property based on the provided payload.

Extra Reducers:

The extraReducers section handles actions dispatched by the thunks defined earlier. It updates the state based on the different stages of the asynchronous actions, such as pending, fulfilled, or rejected. For example, when the fetchLogout.fulfilled action is dispatched, it updates the state to reflect a successful logout.

import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
import Cookies from "js-cookie";
import { logoutAPI } from "../../constants/CommonApiUrls";
import { ApiCallHeaderAndBody } from "../../hooks/ApiCallHeaderAndBody";

const initialState = {
  sidebarFixed: false,
  logout: false,
  userEmail: { userId: "", authenticated: false },
  loadTokenVerify: false,
  token: "",
  tokenError: "",
  permissions: {},
  currentUserInitialCollege: Cookies.get("COLLEGE_ID")
    ? JSON.parse(Cookies.get("COLLEGE_ID"))
    : {},
  currentUserCollegesList: Cookies.get("COLLEGE_LIST")
    ? JSON.parse(Cookies.get("COLLEGE_LIST"))
    : [],
};

// fetching logout Api
export const fetchPermission = createAsyncThunk(
  "/fetchPermission",
  async ({ token, collegeId }) => {
    const response = await fetch(
      `${process.env.REACT_APP_API_BASE_URL}/admin/get_user_permission${collegeId ? "?college_id=" + collegeId : ""}`,
      ApiCallHeaderAndBody(token, "GET")
    ).then((res) => res.json());
    return response;
  }
);

// fetching logout Api
export const fetchLogout = createAsyncThunk("/logout", async (token) => {
  const response = await fetch(
    logoutAPI,
    ApiCallHeaderAndBody(token, "GET")
  ).then((res) => res.json());
  return response;
});
// fetching token info API
export const fetchToken = createAsyncThunk("/fetchToken", async (token) => {
  const response = await fetch(
    `${process.env.REACT_APP_API_BASE_URL}/oauth/tokeninfo?token=${token}`,
    {
      method: "POST",
      headers: {
        accept: "application/json",
      },
    }
  ).then((res) => res.json());
  return response;
});

export const authSlice = createSlice({
  name: "authSlice",
  initialState,
  reducers: {
    setUserEmail: (state, action) => {
      state.userEmail = {
        userId: action.payload.userId,
        authenticated: action.payload.authenticated,
      };
    },
    removeCookies: (state, action) => {
      state.token = "";
      state.userEmail = { userId: "", authenticated: false };
      state.loadTokenVerify = false;
      state.currentUserInitialCollege = {};
      state.currentUserCollegesList = [];
      Cookies.remove("jwtTokenCredentialsRefreshToken");
      Cookies.remove("jwtTokenCredentialsAccessToken");
      Cookies.remove("COLLEGE_ID");
      Cookies.remove("COLLEGE_LIST");
      Cookies.remove("userId");
    },
    setLoadTokenVerify: (state, action) => {
      state.loadTokenVerify = action.payload;
    },
    setSidebarFixed: (state, action) => {
      state.sidebarFixed = action.payload;
    },
    setPermissions: (state, action) => {
      state.permissions = {};
    },
    setTokenInfo: (state, action) => {
      state.token = action.payload;
    },
    setUserCollegeInfo: (state, action) => {
      state.currentUserInitialCollege = action?.payload?.initialCollege;
      state.currentUserCollegesList = action?.payload?.collegeList;
    },
  },
  extraReducers: (builder) => {
    // set logout
    builder.addCase(fetchLogout.fulfilled, (state, action) => {
      state.userEmail = { userId: "", authenticated: false };
      state.logout = true;
    });
    builder.addCase(fetchLogout.pending, (state, action) => {
      state.logout = false;
    });

    builder.addCase(fetchLogout.rejected, (state, action) => {
      state.logout = false;
    });
    builder.addCase(fetchPermission.fulfilled, (state, action) => {
      if (action?.payload?.detail === "Could not validate credentials") {
        window.location.reload();
      } else {
        state.permissions = action?.payload?.data[0];
      }
    });
    builder.addCase(fetchPermission.pending, (state, action) => { });

    builder.addCase(fetchPermission.rejected, (state, action) => {
      state.logout = false;
    });
    builder.addCase(fetchToken.fulfilled, (state, action) => {
      state.token = action.payload;
    });
    builder.addCase(fetchToken.pending, (state, action) => { });

    builder.addCase(fetchToken.rejected, (state, action) => {
      state.token = { server: "500" };
    });
  },
});

// Action creators are generated for each case reducer function
export const {
  setUserEmail,
  removeCookies,
  setLoadTokenVerify,
  setPermissions,
  setSidebarFixed,
  setTokenInfo,
  setUserCollegeInfo,
} = authSlice.actions;

export default authSlice.reducer;
Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE BLURT!