如何使用 react-router-dom 创建受保护的路由?

问题描述 投票:0回答:5

如何使用

react-router-dom
创建受保护的路由并将响应存储在 localStorage 中,以便用户下次尝试打开时可以再次查看其详细信息。登录后,他们应该重定向到仪表板页面。

所有功能都添加到 ContextApi 中。 代码沙盒链接:代码

我试过但没能实现

路线页面

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;

上下文页面

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>
    );
  }
}
javascript reactjs react-router react-router-dom
5个回答
32
投票

问题

<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
不处理除
Route
Redirect
组件之外的任何渲染。如果你想像这样“嵌套”,那么你需要将每个包裹在通用路线中,但这是完全没有必要的。

您的登录组件也不处理重定向回任何“主页”页面或最初访问的私有路由。

解决方案

react-router-dom
v5

Create a

PrivateRoute
component that consumes your auth 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 }
      }}
    />
  );
};

更新您的

Login
组件以处理重定向回正在访问的原始路由。

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>
  );
}

在“平面列表”中呈现所有路线

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>
  );
}

Edit how-to-create-a-protected-route-in-react-using-react-router-dom

react-router-dom
v6

在版本 6 中,自定义路由组件已经失宠,首选方法是使用 auth 布局组件。

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>

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>
  );
}

5
投票

对于 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} />;
}

文档链接: https://gist.github.com/mjackson/d54b40a094277b7afdd6b81f51a0393f


0
投票

import { v4 as uuidv4 } from "uuid";

const routes = [
    {
        id: uuidv4(),
        isProtected: false,
        exact: true,
        path: "/home",
        component: param => <Overview {...param} />,
    },
    {
        id: uuidv4(),
        isProtected: true,
        exact: true,
        path: "/protected",
        component: param => <Overview {...param} />,
        allowed: [...advanceProducts], // subscription
    },
    {
        // if you conditional based rendering for same path
        id: uuidv4(),
        isProtected: true,
        exact: true,
        path: "/",
        component: null,
        conditionalComponent: true,
        allowed: {
            [subscription1]: param => <Overview {...param} />,
            [subscription2]: param => <Customers {...param} />,
        },
    },
]

// Navigation Component
import React, { useEffect, useState } from "react";
import { useSelector } from "react-redux";
import { Switch, Route, useLocation } from "react-router-dom";

// ...component logic
<Switch>
    {routes.map(params => {
        return (
            <ProtectedRoutes
                exact
                routeParams={params}
                key={params.path}
                path={params.path}
            />
        );
    })}
    <Route
        render={() => {
            props.setHideNav(true);
            setHideHeader(true);
            return <ErrorPage type={404} />;
        }}
    />
</Switch>

// ProtectedRoute component
import React from "react";
import { Route } from "react-router-dom";
import { useSelector } from "react-redux";

const ProtectedRoutes = props => {
    const { routeParams } = props;
    const currentSubscription = 'xyz'; // your current subscription;
    if (routeParams.conditionalComponent) {
        return (
            <Route
                key={routeParams.path}
                path={routeParams.path}
                render={routeParams.allowed[currentSubscription]}
            />
        );
    }
    if (routeParams.isProtected && routeParams.allowed.includes(currentSubscription)) {
        return (
            <Route key={routeParams.path} path={routeParams.path} render={routeParams?.component} />
        );
    }
    if (!routeParams.isProtected) {
        return (
            <Route key={routeParams.path} path={routeParams.path} render={routeParams?.component} />
        );
    }
    return null;
};

export default ProtectedRoutes;

想要添加亮点永远不要忘记将路径作为 ProtectedRoute 的道具,否则它将无法工作。


0
投票

这是一个简单的 react-router v6 保护路由。我已经把所有我想保护的路线放在routes.js:-

const routes = [{ path: "/dasboard", name:"Dashboard", element: <Dashboard/> }]

要渲染路线,只需将它们映射如下:-

<Routes>
   {routes.map((routes, id) => {
     return(
     <Route
         key={id}
         path={route.path}
         exact={route.exact}
         name={route.name}
         element={
            localStorage.getItem("token") ? (
                route.element
             ) : (
                <Navigate to="/login" />
             )
         }
     )
   })
  }
</Routes>

0
投票

如果你想要一个简单的方法来实现然后使用 App.js 中的登录,如果用户登录然后设置用户变量。 如果设置了用户变量,则启动这些路由,否则它将停留在登录页面。我在我的项目中实现了这个。

return (
<div>
  <Notification notification={notification} type={notificationType} />

  {
    user === null &&
    <LoginForm startLogin={handleLogin} />
  }

  {
    user !== null &&
    <NavBar user={user} setUser={setUser} />
  }

  {
    user !== null &&
    <Router>
      <Routes>
        <Route exact path="/" element={<Home />} />
        <Route exact path="/adduser" element={<AddUser />} /> />
        <Route exact path="/viewuser/:id" element={<ViewUser />} />
      </Routes>
    </Router>
  }

</div>

)

© www.soinside.com 2019 - 2024. All rights reserved.