search

Home  >  Q&A  >  body text

Create secure routes using react-router-dom

How to use react-router-domCreate a protected route and store the response in localStorage so that the user can view its details again the next time it is opened. After logging in, you should be redirected to the dashboard page.

All functions are added in ContextApi.

Codesandbox link: Code

I tried, but couldn't achieve it.

Routing page

import React, { useContext } from "react";
import { globalC } from "./context";
import { Route, Switch, BrowserRouter } from "react-router-dom";
import About from "./About";
import Dashboard from "./Dashboard";
import Login from "./Login";
import PageNotFound from "./PageNotFound";

function Routes() {
  const { authLogin } = useContext(globalC);
  console.log("authLogin", authLogin);

  return (
    <BrowserRouter>
      <Switch>
        {authLogin ? (
          <>
            <Route path="/dashboard" component={Dashboard} exact />
            <Route exact path="/About" component={About} />
          </>
        ) : (
          <Route path="/" component={Login} exact />
        )}

        <Route component={PageNotFound} />
      </Switch>
    </BrowserRouter>
  );
}
export default Routes;

Context page

import React, { Component, createContext } from "react";
import axios from "axios";

export const globalC = createContext();

export class Gprov extends Component {
  state = {
    authLogin: null,
    authLoginerror: null
  };
  componentDidMount() {
    var localData = JSON.parse(localStorage.getItem("loginDetail"));
    if (localData) {
      this.setState({
        authLogin: localData
      });
    }
  }

  loginData = async () => {
    let payload = {
      token: "ctz43XoULrgv_0p1pvq7tA",
      data: {
        name: "nameFirst",
        email: "internetEmail",
        phone: "phoneHome",
        _repeat: 300
      }
    };
    await axios
      .post(`https://app.fakejson.com/q`, payload)
      .then((res) => {
        if (res.status === 200) {
          this.setState({
            authLogin: res.data
          });
          localStorage.setItem("loginDetail", JSON.stringify(res.data));
        }
      })
      .catch((err) =>
        this.setState({
          authLoginerror: err
        })
      );
  };
  render() {
    // console.log(localStorage.getItem("loginDetail"));
    return (
      <globalC.Provider
        value={{
          ...this.state,
          loginData: this.loginData
        }}
      >
        {this.props.children}
      </globalC.Provider>
    );
  }
}

P粉156983446P粉156983446436 days ago614

reply all(2)I'll reply

  • P粉122932466

    P粉1229324662023-09-22 12:07:30

    For v6:

    import { Routes, Route, Navigate } from "react-router-dom";
    
    function App() {
      return (
        <Routes>
          <Route path="/public" element={<PublicPage />} />
          <Route
            path="/protected"
            element={
              <RequireAuth redirectTo="/login">
                <ProtectedPage />
              </RequireAuth>
            }
          />
        </Routes>
      );
    }
    
    function RequireAuth({ children, redirectTo }) {
      let isAuthenticated = getAuth();
      return isAuthenticated ? children : <Navigate to={redirectTo} />;
    }

    Link to documentation: https://gist.github.com/mjackson/d54b40a094277b7afdd6b81f51a0393f

    reply
    0
  • P粉587780103

    P粉5877801032023-09-22 00:11:42

    question

    <BrowserRouter>
      <Switch>
        {authLogin ? (
          <>
            <Route path="/dashboard" component={Dashboard} exact />
            <Route exact path="/About" component={About} />
          </>
        ) : (
          <Route path="/" component={Login} exact />
        )}
    
        <Route component={PageNotFound} />
      </Switch>
    </BrowserRouter>

    Switch does not handle any rendering except for Route and Redirect components. If you wanted to "nest" like this, then you would need to wrap each component in a common route, but this is completely unnecessary.

    Your login component also does not handle redirecting back to the "homepage" or private route originally visited.

    solution

    react-router-dom v6

    In version 6, custom routing components are no longer popular, and the recommended method is to use the authentication layout component.

    import { Navigate, Outlet } from 'react-router-dom';
    
    const PrivateRoutes = () => {
      const location = useLocation();
      const { authLogin } = useContext(globalC);
    
      if (authLogin === undefined) {
        return null; // or loading indicator/spinner/etc
      }
    
      return authLogin 
        ? <Outlet />
        : <Navigate to="/login" replace state={{ from: location }} />;
    }

    ...

    <BrowserRouter>
      <Routes>
        <Route path="/" element={<PrivateRoutes />} >
          <Route path="dashboard" element={<Dashboard />} />
          <Route path="about" element={<About />} />
        </Route>
        <Route path="/login" element={<Login />} />
        <Route path="*" element={<PageNotFound />} />
      </Routes>
    </BrowserRouter>

    or

    const routes = [
      {
        path: "/",
        element: <PrivateRoutes />,
        children: [
          {
            path: "dashboard",
            element: <Dashboard />,
          },
          {
            path: "about",
            element: <About />
          },
        ],
      },
      {
        path: "/login",
        element: <Login />,
      },
      {
        path: "*",
        element: <PageNotFound />
      },
    ];

    ...

    export default function Login() {
      const location = useLocation();
      const navigate = useNavigate();
      const { authLogin, loginData } = useContext(globalC);
    
      useEffect(() => {
        if (authLogin) {
          const { from } = location.state || { from: { pathname: "/" } };
          navigate(from, { replace: true });
        }
      }, [authLogin, location, navigate]);
    
      return (
        <div
          style={{ height: "100vh" }}
          className="d-flex justify-content-center align-items-center"
        >
          <button type="button" onClick={loginData} className="btn btn-primary">
            Login
          </button>
        </div>
      );
    }

    react-router-dom v5

    Create a PrivateRoute component that consumes your authentication context.

    const PrivateRoute = (props) => {
      const location = useLocation();
      const { authLogin } = useContext(globalC);
    
      if (authLogin === undefined) {
        return null; // or loading indicator/spinner/etc
      }
    
      return authLogin ? (
        <Route {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/login",
            state: { from: location }
          }}
        />
      );
    };

    Update your Login component to handle redirects back to the original access route.

    export default function Login() {
      const location = useLocation();
      const history = useHistory();
      const { authLogin, loginData } = useContext(globalC);
    
      useEffect(() => {
        if (authLogin) {
          const { from } = location.state || { from: { pathname: "/" } };
          history.replace(from);
        }
      }, [authLogin, history, location]);
    
      return (
        <div
          style={{ height: "100vh" }}
          className="d-flex justify-content-center align-items-center"
        >
          <button type="button" onClick={loginData} className="btn btn-primary">
            Login
          </button>
        </div>
      );
    }

    Render all routes in a "flat list"

    function Routes() {
      return (
        <BrowserRouter>
          <Switch>
            <PrivateRoute path="/dashboard" component={Dashboard} />
            <PrivateRoute path="/About" component={About} />
            <Route path="/login" component={Login} />
            <Route component={PageNotFound} />
          </Switch>
        </BrowserRouter>
      );
    }

    reply
    0
  • Cancelreply