2

I am using React.js and react-router-dom version 6. I have set up a private Outlet and made redirecting functionality works.

The problem is if the user is not authenticated and tries to go to the homepage, React shows the homepage but immediately sends the user to the login page. Everything happens in a second. I don't want to show the homepage.

// private Outlet

import axios from "axios";
import { useEffect, useState } from "react";
import { Navigate, Outlet } from "react-router-dom";

export default function PrivateOutlet() {
  const [user, setUser] = useState(true);
  const [isLoading, setIsLoading] = useState(true);

  const getUser = async () => {
    try {
      const { data } = await axios.get(
        `${process.env.REACT_APP_API_URL}auth/login/success`,
        {
          withCredentials: true,
        }
      );
      console.log(data.isLoggedIn);
      setUser(data);
    } catch (error) {
      setUser(false);
    }
  };
  useEffect(() => {
    getUser();
    setIsLoading(false);
  }, []);

  if (isLoading) return <h1>loading</h1>;

  return user ? <Outlet /> : <Navigate to="/login" />;
}

// APP.js

import "./App.css";
import { BrowserRouter, Routes, Route } from "react-router-dom";
import Home from "./pages/Home";
import Login from "./pages/Login";
import PrivateOutlet from "./components/PrivateOutlet";

function App() {
  return (
    <BrowserRouter>
      <div>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route element={<PrivateOutlet />}>
            <Route path="/" element={<Home />} />
          </Route>
        </Routes>
      </div>
    </BrowserRouter>
  );
}

export default App;

Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65
Sajib Hossain
  • 81
  • 2
  • 8
  • You have to set default value of user state to false but in the code you had set user to true where user state is initialized. So when non authorised user tries to access homepage initially system detects user is present (as you set true in default) and shows home page with that it also starts api call and on failed api response it values changed to false and user sees Login page. – Rohit Kumar Jan 28 '23 at 17:55
  • Also change loading state value to false when api call complete, not in useEffect. When page loads then API gets called and simultaniously loading value set to false as it in useEffect, it does does not wait for api to complete here. – Rohit Kumar Jan 28 '23 at 17:59
  • Hi Sajib, @RohitKumar makes good points. I posted an answer with implementation; check it and let me know :) – Youssouf Oumar Jan 28 '23 at 18:31
  • @yousoumar yes that is correct. – Rohit Kumar Jan 28 '23 at 18:36

1 Answers1

2

In your useEffect, you are calling setIsLoading(false) just after getUser(), so the loading state is changed before the API call completes. You could move setIsLoading(false) inside getUser in a finally block, like so:

import axios from "axios";
import { useEffect, useState } from "react";
import { Navigate, Outlet } from "react-router-dom";

export default function PrivateOutlet() {
  // set the user to null at first
  const [user, setUser] = useState(null);
  const [isLoading, setIsLoading] = useState(true);

  useEffect(() => {
    const getUser = async () => {
      try {
        const { data } = await axios.get(`${process.env.REACT_APP_API_URL}auth/login/success`, {
          withCredentials: true,
        });
        console.log(data.isLoggedIn);
        setUser(data);
      } catch (error) {
        // you can set some error message state here and show it to the user if you want.
        console.log(error);
      } finally {
        setIsLoading(false);
      }
    };

    getUser();
  }, []);

  if (isLoading) return <h1>loading</h1>;

  return user ? <Outlet /> : <Navigate to="/login" />;
}

Notice I also changed the initial value of the user state to null, and changing this value only when the request is a success.

Youssouf Oumar
  • 29,373
  • 11
  • 46
  • 65