skip to Main Content

I just started using React. I’m building a CRUD app with it. I made some pages that can be browsed using react-router-dom with authentication. If the user is logged in, they are able to see those pages. Otherwise, we will be redirected to the sign-in page.

Everything works fine except for refreshing pages. When I’m at any page and click "refresh" in the browser, it should reload the last page we are currently at. But instead, the app re-renders and eventually goes back to the homepage. This is not what was intended.

import React, { useEffect, useState } from "react";
import "./app.css";
import Sidebar from "./components/sidebar/Sidebar";
import Topbar from "./components/topbar/Topbar";
import Home from "./pages/Home/Home";
import {
  BrowserRouter as Router,
  Route,
  Routes,
  Navigate,
} from "react-router-dom";
import Users from "./pages/Users/Users";
import Patients from "./pages/Patients/Patients";
import PatientDetails from "./pages/PatientDetails/PatientDetails";
import AccountSettings from "./pages/AccountSettings/AccountSettings";
import SignIn from "./pages/SignIn/SignIn";
import UpdateUserForm from "./pages/UserForm/UpdateUserForm";
import CreateUserForm from "./pages/UserForm/CreateUserForm";
import CreatePatientForm from "./pages/PatientForm/CreatePatientForm";
import UpdatePatientForm from "./pages/PatientForm/UpdatePatientForm";
import CreateAppointmentForm from "./pages/AppointmentForm/CreateAppointmentForm";
import UpdateAppointmentForm from "./pages/AppointmentForm/UpdateAppointmentForm";
import axios from "axios";
import UserContext from "./utils/UserContext";

// use react-router before v.6.5.0

export default function App() {
  const [user, setUser] = useState(false);

  const getLoginStatus = async () => {
    const data = localStorage.getItem("DENTAL_ART_LOGGED_USER");
    console.log(data);
    if (data) {
      const res = await axios.get("http://localhost:3001/api/login/status", {
        params: JSON.parse(data),
      });

      if (res.data) {
        setUser(res.data);
      }
    }
  };

  const postUserLogin = async (userValues) => {
    try {
      const res = await axios.post(
        "http://localhost:3001/api/signin",
        userValues
      );

      setUser(res.data.user);
      // save user to localStorage
      localStorage.setItem("DENTAL_ART_LOGGED_USER", JSON.stringify(res.data));
    } catch (error) {
      console.log(error);
    }
  };

  const getUserLogout = async (e) => {
    // grab user session information on localStorage, passed it to server as an information to delete that session in DB.
    const data = localStorage.getItem("DENTAL_ART_LOGGED_USER");
    try {
      const res = await axios.get("http://localhost:3001/api/signout", {
        params: JSON.parse(data),
      });
      // reset the user state (value: false)
      setUser(res.data);
      localStorage.removeItem("DENTAL_ART_LOGGED_USER");

      getLoginStatus();
    } catch (error) {
      console.log(error);
    }
  };

  useEffect(() => {
    if (!user) {
      getLoginStatus();
    }
  }, []);

  return (
    <UserContext.Provider value={{ user, postUserLogin, getUserLogout }}>
      <Router>
        <Routes>
          <Route
            path="/signin"
            element={user ? <Navigate to="/" /> : <SignIn />}
          />
          <Route
            path="*"
            element={
              <>
                <Topbar />
                <div className="container">
                  <Sidebar />
                  <Routes>
                    <Route
                      index
                      exact
                      path="/"
                      element={user ? <Home /> : <Navigate to="/signin" />}
                    />
                    <Route
                      exact
                      path="/patients"
                      element={user ? <Patients /> : <Navigate to="/signin" />}
                    />
                    <Route
                      exact
                      path="/users"
                      element={user ? <Users /> : <Navigate to="/signin" />}
                    />
                    <Route
                      exact
                      path="/patient/:patientId"
                      element={
                        user ? <PatientDetails /> : <Navigate to="/signin" />
                      }
                    />
                    ;
                    <Route
                      exact
                      path="/patient/:patientId?/appointmentForm/create"
                      element={
                        user ? (
                          <CreateAppointmentForm />
                        ) : (
                          <Navigate to="/signin" />
                        )
                      }
                    />
                    <Route
                      exact
                      path="/patient/:patientId?/appointmentForm/:appointmentId?/edit"
                      element={
                        user ? (
                          <UpdateAppointmentForm />
                        ) : (
                          <Navigate to="/signin" />
                        )
                      }
                    />
                    <Route
                      exact
                      path="/accountSettings"
                      element={
                        user ? <AccountSettings /> : <Navigate to="/signin" />
                      }
                    />
                    <Route
                      exact
                      path="/userForm/:userId?/edit"
                      element={
                        user ? <UpdateUserForm /> : <Navigate to="/signin" />
                      }
                    />
                    <Route
                      exact
                      path="/userForm/create"
                      element={
                        user ? <CreateUserForm /> : <Navigate to="/signin" />
                      }
                    />
                    <Route
                      exact
                      path="/patientForm/create"
                      element={
                        user ? <CreatePatientForm /> : <Navigate to="/signin" />
                      }
                    />
                    <Route
                      exact
                      path="/patientForm/:patientId?/edit"
                      element={
                        user ? <UpdatePatientForm /> : <Navigate to="/signin" />
                      }
                    />
                  </Routes>
                </div>
              </>
            }
          />
        </Routes>
      </Router>
    </UserContext.Provider>
  );
}

Some resources suggested using localStorage to save the current route/path using the useLocation hook, so whenever the path we are currently at changes, the browser will save that path to localStorage, and it will get it back when refreshes happen.

But I’m not sure that is the best practice, since I don’t find useLocation can be implemented inside the React App component.

2

Answers


  1. You should handle authenticate user in pages Home, Patient using useEffect inside that page (or better use a HOC to wrap the pages).

    Because whenever you refresh, user state will be reset to false to you’ll be navigate to /signin route, then when successfully authenticated, you’ll be redirect to home

    <Route
     path="/signin"
      element={user ? <Navigate to="/" /> : <SignIn />}
    />
    
    Login or Signup to reply.
  2. Issue

    Since the callback of an useEffect runs after the JSX is rendered, on refresh, the user state is false, that default value given to useState. So the page you are currently on redirects to "/signin". And meanwhile, getLoginStatus finishes running, and user is set to true, so you are redirected back to "/".

    Solution

    The easiest way to solve this issue, and that adds little changes to your current code, is to set up a loading state so you render the JSX only after getLoginStatus finish running. Like so:

    Notice the comments.

    // ...
    
    export default function App() {
      const [user, setUser] = useState(false);
      // New State Addded
      const [checking, setChecking] = useState(true);
    
      // ...
    
      useEffect(() => {
        if (!user) {
          // Line Changed
          getLoginStatus().then(() => {
            setChecking(false);
          });
        }
      }, []);
    
      // A New Check Added
      if (checking) {
        // Usually, you would return a loading indicator
        return null;
      }
    
      return (
        <UserContext.Provider value={{ user, postUserLogin, getUserLogout }}>
          {/* Your current code */}
        </UserContext.Provider>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search