【问题标题】:How to create a protected route?如何创建受保护的路由?
【发布时间】:2021-05-23 03:02:10
【问题描述】:

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

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

我试过了,但没能做到

路线页面

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

【问题讨论】:

标签: reactjs react-router react-router-dom


【解决方案1】:

问题

<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 不处理除RouteRender 组件之外的任何内容。如果您想像这样“嵌套”,则需要将每个路由包装在通用路由中,但这完全没有必要。

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

解决方案

react-router-domv5

创建一个使用您的身份验证上下文的PrivateRoute 组件。

const PrivateRoute = (props) => {
  const location = useLocation();
  const { authLogin } = useContext(globalC);
  console.log("authLogin", authLogin);

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

  if (authLogin) {
    const { from } = location.state || { from: { pathname: "/" } };
    history.replace(from)
  }

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

react-router-domv6

在版本 6 中自定义路由组件已经失宠,首选方法是使用身份验证包装器组件。

import { Navigate, Outlet } from 'react-router-dom';

const PrivateRoutes = () => {
  const location = useLocation();
  const { authLogin } = useContext(globalC);
  console.log("authLogin", authLogin);

  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 element={<PageNotFound />} />
  </Routes>
</BrowserRouter>

【讨论】:

  • 您好,先生。我试过你的代码,它工作正常。但我面临一些问题。对我来说,没有主页,登录页面是默认页面,一旦用户尝试根据响应登录,它将导航到仪表板,并且侧边栏仅在受保护的路线中可用。密码沙盒链接:codesandbox.io/s/protuctedroute-d26tz@Drew
  • @learner62 我沙箱中的“home”路由只是为了提供一个没有尝试启动身份验证流程的起点。不过,我会警告不要将您的“/”路径作为身份验证路径。话虽如此,您的沙箱似乎正在工作,您只需要确保您没有在登录路径“/”和重定向回“/”的PrivateRoute 之间创建渲染循环。
  • 如果您登录并在受保护的路径刷新页面,它将使用上面的代码将您从页面中移除。这是因为在刷新期间上下文最初为空,然后才从数据库中水合。有关如何纠正此问题的任何建议?
  • @Ufeneiaugustine 添加第三个加载或挂起的“状态”,它既不是“身份验证”也不是“未经身份验证”,您可以渲染 null、加载微调器或任何其他“处理”UI,直到它确定是否应允许用户进入该页面,或者是否应将其退回。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-11-16
  • 2020-07-23
相关资源
最近更新 更多