0

I'm designing a ecommerce app wherein I'm implementing login persistence by sending api request to loadUser function(which in turn calls loadUser endpoint which is a controller written in express).

In App.js. I'm doing this to retain user data every time the application is refreshed(unless the user logs out then the token is removed which logs the user out). The token is fetched through cookies and the authentication is handled in the backend as required.

The problem I'm currently facing is the issue of loading states and with routing between different pages in the application. I have several protected routes which receive isAuthfrom the redux state(isAuthenticated is true when loadUser end point is successfull). The issue here is that the initial isAuthenticated value in redux i.e false is being retained before the loadUser function is dispatched and as such I'm constantly being routed back to /'. How to handle this problem?

Is this method of login persistence correct or is there any other better method to handle this?

This is my App.js and protected Route component

import React, { useCallback, useEffect, useState } from "react";
import { Box, Button, Center, Spinner, Text } from "@chakra-ui/react";
import webfont, { load } from "webfontloader";
import { Routes, Route } from "react-router-dom";
import Home from "./Pages/Home";
import Register from "./Pages/Register";
import Login from "./Pages/Login";
import Products from "./Pages/Products";
import ProductDetails from "./Pages/ProductDetails";
import Cart from "./Pages/Cart";
import Wishlist from "./Pages/Wishlist";
import Profile from "./Pages/Profile";
import Dashboard from "./Pages/dashboard/Dashboard";
import AdminUsers from "./Pages/dashboard/AdminUsers";
import AdminProducts from "./Pages/dashboard/AdminProducts";
import AdminOrders from "./Pages/dashboard/AdminOrders";
import { useDispatch, useSelector } from "react-redux";
import { loadUser } from "./Redux/slices/userSlice";
import UserProtectedRoute from "./Components/ProtectedRoutes/userProtectedRoute";
import AdminProtectedRoute from "./Components/ProtectedRoutes/adminProtectedRoute";

const App = () => {
  const dispatch = useDispatch();
  const [initial, setInitial] = useState(true);

  const loading = useSelector((state) => state.user?.loadUser?.isLoading);
  useEffect(() => {
    console.log("Use effected called ");
    webfont.load({
      google: {
        families: ["Roboto:500", "Pacifico", "sans-serif"],
      },
    });
    dispatch(loadUser());
  }, [dispatch]);
  if (loading) {
    return (
      <>
        <Center minH="40rem">
          <Spinner color="red" size={"xl"} />
        </Center>
      </>
    );
  }
  return (
    <div>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/login" element={<Login />} />
        <Route path="/register" element={<Register />} />
        <Route path="/products" element={<Products />} />
        <Route path="/products/:id" element={<ProductDetails />} />
        <Route
          path="/cart"
          element={
            <UserProtectedRoute>
              <Cart />
            </UserProtectedRoute>
          }
        />
        <Route
          path="/wishlist"
          element={
            <UserProtectedRoute>
              <Wishlist />
            </UserProtectedRoute>
          }
        />
        <Route
          path="/profile"
          element={
            <UserProtectedRoute>
              <Profile />
            </UserProtectedRoute>
          }
        />
       
       
       
      </Routes>
    </div>
  );
};

export default App;



import React from "react";
import { useSelector } from "react-redux";
import { Navigate } from "react-router-dom";

const UserProtectedRoute = ({ children }) => {
  const isAuth = useSelector((state) => state.user?.isAuthenticated);
  console.log(isAuth);
  if (!isAuth) {
    return <Navigate to={"/"} replace />;
  }
  return children;
};

export default UserProtectedRoute;

This is my redux-toolkit setup of initialState, loadUser asyncthunk function and addCases.

const initialState = {
  isAuthenticated: false,
  userInfo: null,
  userRegister: {
    isLoading: false,
    isError: false,
    data: null,
    error: null,
  },
  userLogin: {
    isLoading: false,
    isError: false,
    isSuccess: false,
    error: null,
  },
  loadUser: {
    isLoading: false,
    isError: false,
    error: null,
  },
  logoutUser: {
    isLoading: false,
    isError: false,
    error: null,
  },
};



export const loadUser = createAsyncThunk(
  "user/loadUser",
  async (_, { rejectWithValue }) => {
    try {
      const { data } = await api.get("/auth/load-user");
      return data;
    } catch (error) {
      console.log(error, "Load error");
      return rejectWithValue({
        status: error.response.status,
        message: error.response.data.message,
      });
    }
  }
);

.addCase(loadUser.pending, (state, action) => {
        state.loadUser.isError = false;
        state.loadUser.isLoading = true;
      })
      .addCase(loadUser.fulfilled, (state, action) => {
        state.loadUser.isLoading = false;
        state.userInfo = action.payload;
        state.isAuthenticated = true;
      })
      .addCase(loadUser.rejected, (state, action) => {
        state.loadUser.isLoading = false;
        state.loadUser.isError = true;
        state.loadUser.error = action.payload;
      })

It has become something like a race condition wherein the isAuth from the protected route is first read as false(before the loadUser function is dispatched) and I'm being routed back to home page constantly again and again. Am I doing user persistence correctly or is there something wrong? Please help!!

Kaneki21
  • 1,323
  • 2
  • 4
  • 22

0 Answers0