Search code examples
reactjsreact-routerredux-toolkit

React Router v6.10 Protected Route problem


I'm trying to build something with react that has some protected routes.

My main.jsx file:

import React from "react";
import ReactDOM from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import "./index.css";

import store from "./app/store";
import { Provider } from "react-redux";

import Root from "./components/Root";
import PublicLayout from "./components/PublicLayout";
import PersistLogin from "./components/PersistLogin";
import RequireAuthLayout from "./components/RequireAuthLayout";
import { ErrorPage, Home, Login, Logout, Register } from "./pages";

const router = createBrowserRouter([
{
path: "/*",
element: <Root />,
errorElement: <ErrorPage />,
children: [
  {
    element: <PublicLayout />,
    children: [
      { path: "login", element: <Login /> },
      { path: "register", element: <Register /> },
    ],
  },
  {
    element: <PersistLogin />,
    children: [
      {
        element: <RequireAuthLayout />,
        children: [
          { path: "home", element: <Home /> },
          { path: "logout", element: <Logout /> },
        ],
      },
    ],
  },
],
  },
]);


ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <Provider store={store}>
      <RouterProvider router={router} />
    </Provider>
  </React.StrictMode>
);

Root.jsx:

import React from "react";
import { Outlet } from "react-router-dom";
import Header from "./Header";

const Root = () => {
  return (
    <>
      <Header />
      <main>
        <Outlet />
      </main>
    </>
  );
};

export default Root;

RequireAuthLayout.jsx:

import React from "react";
import { useLocation, Navigate, Outlet } from "react-router-dom";
import { useSelector } from "react-redux";
import { selectCurrentToken } from ".././features/auth/authSlice";

const RequireAuthLayout = () => {
  const token = useSelector(selectCurrentToken);
  const location = useLocation();

  return token ? (
    <Outlet />
  ) : (
    <Navigate to={"/login"} state={{ from: location }} replace />
  );
};

export default RequireAuthLayout;

PersistLogin.jsx:

import React, { useEffect } from "react";
import { Outlet } from "react-router-dom";
import { useDispatch, useSelector } from "react-redux";
import { useRefreshMutation } from "../features/api/authApiSlice";
import { selectCurrentToken, setCredentials } from "../features/auth/authSlice";
import Loading from "../components/Loading";

const PersistLogin = () => {
  const [refresh, { isLoading, isError }] = useRefreshMutation();
  const dispatch = useDispatch();

  const token = useSelector(selectCurrentToken);

  let content;

  useEffect(() => {
    const getAccessToken = async () => {
      try {
        const result = await refresh();
        dispatch(setCredentials(result.data));
      } catch (error) {
        // console.log(error);
      }
    };
    !token && getAccessToken();
  }, []);

  if (isLoading) {
    content = <Loading />;
  } else if (isError) {
    content = <p>Bir hata oluştu, tekrar giriş yapınız!</p>;
  } else {
    <Outlet />;
  }

  return content;
};

export default PersistLogin;

PersistLogin is just sending a request to the endpoint to check if there is a cookie. If there is and valid it sets the token in the redux store. But unfourtunately protected routes are not rendering. I mean there is no error and anything but they are not rendering. The token is okey and everything but stil..

Please help me..


Solution

  • The PersistLogin component appears to not be rendering an Outlet component for the nested routes to render their content into. <Outlet /> should be returned.

    const PersistLogin = () => {
      const [refresh, { isLoading, isError }] = useRefreshMutation();
      const dispatch = useDispatch();
    
      const token = useSelector(selectCurrentToken);
    
      let content;
    
      useEffect(() => {
        const getAccessToken = async () => {
          try {
            const result = await refresh();
            dispatch(setCredentials(result.data));
          } catch (error) {
            // console.log(error);
          }
        };
        !token && getAccessToken();
      }, []);
    
      if (isLoading) {
        content = <Loading />;
      } else if (isError) {
        content = <p>Bir hata oluştu, tekrar giriş yapınız!</p>;
      } else {
        content = <Outlet />; // <-- set content
      }
    
      return content;
    };
    

    or

    const PersistLogin = () => {
      const [refresh, { isLoading, isError }] = useRefreshMutation();
      const dispatch = useDispatch();
    
      const token = useSelector(selectCurrentToken);
    
      let content <Outlet />; // <-- set as "default" content;
    
      useEffect(() => {
        const getAccessToken = async () => {
          try {
            const result = await refresh();
            dispatch(setCredentials(result.data));
          } catch (error) {
            // console.log(error);
          }
        };
        !token && getAccessToken();
      }, []);
    
      if (isLoading) {
        content = <Loading />;
      } else if (isError) {
        content = <p>Bir hata oluştu, tekrar giriş yapınız!</p>;
      }
    
      return content;
    };
    

    or

    const PersistLogin = () => {
      const [refresh, { isLoading, isError }] = useRefreshMutation();
      const dispatch = useDispatch();
    
      const token = useSelector(selectCurrentToken);
    
      useEffect(() => {
        const getAccessToken = async () => {
          try {
            const result = await refresh();
            dispatch(setCredentials(result.data));
          } catch (error) {
            // console.log(error);
          }
        };
        !token && getAccessToken();
      }, []);
    
      if (isLoading) {
        return <Loading />;
      }
      if (isError) {
        return <p>Bir hata oluştu, tekrar giriş yapınız!</p>;
      }
      return <Outlet />;
    };