search

Home  >  Q&A  >  body text

Creating protected routes: a how-to guide using react-router-dom

<p>How to use <code>react-router-dom</code> to create a protected route and store the response in localStorage so the user can view their details again the next time they open it. After logging in, they should be redirected to the dashboard page. </p> <p>All functions are added in ContextApi.Codesandbox链接:Code</p> <p>我尝试过,但无法实现</p> <p>路由页面</p> <pre class="brush:php;toolbar:false;">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;</pre> <p>上下文页面</p> <pre class="brush:php;toolbar:false;">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> ); } }</pre> <p><br /></p>
P粉596191963P粉596191963463 days ago483

reply all(2)I'll reply

  • P粉253518620

    P粉2535186202023-08-25 09:42:47

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

    Documentation link: https://gist.github.com/mjackson/d54b40a094277b7afdd6b81f51a0393f

    reply
    0
  • P粉505917590

    P粉5059175902023-08-25 00:11:28

    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>

    SwitchNo rendering will be handled except for the Route and Redirect components. If you wanted to "nest" like this, then you would need to wrap each component in a generic route, but this is completely unnecessary.

    Your login component has not yet handled redirecting back to the original visited "home" or private route.

    solution

    react-router-dom v6

    In version 6, custom routing components are no longer popular, and the preferred method is to use auth layout components.

    import { Navigate, Outlet } from 'react-router-dom';
    
    const PrivateRoutes = () => {
      const location = useLocation();
      const { authLogin } = useContext(globalC);
    
      if (authLogin === undefined) {
        return null; // 或者加载指示器/旋转器等
      }
    
      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">
            登录
          </button>
        </div>
      );
    }

    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; // 或者加载指示器/旋转器等
      }
    
      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">
            登录
          </button>
        </div>
      );
    }

    Render all routes as 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