簡體   English   中英

受限導航 React Router Dom - 私有和公共路由

[英]Restricted navigation React Router Dom - Private and Public routes

我需要制作一個區分經過身份驗證和未經身份驗證的用戶的路由器系統。

我想要實現的是,經過身份驗證的用戶可以看到他們的儀表板和個人詳細信息,但未經身份驗證的用戶只能看到登錄頁面、登錄表單和注冊表單。

我目前有上下文管理,所以我知道用戶是否登錄。 我只需要弄清楚所有重定向和路由相關的事情。

我的代碼正在制作各種“奇怪”的東西。 我真的不知道我在哪里搞砸了。

應用內容.js

這是所有路由發生的地方。 AppLayout只是一個布局包裝器,帶有 header 和一個僅在登錄時才可見的頁腳。

const AppContent = () => (
    <Router>
        <Switch>
            <PublicRoute component={() => (<h1>Landing page</h1>)} path='/' exact />
            <PublicRoute component={LoginPage} path='/login' exact />
            <PublicRoute component={() => (<h1>Register page</h1>)} path='/register' exact />

            <PrivateRoute component={Home} path='/dashboard' exact />
            <PrivateRoute component={Demo} path='/demo' exact />
            <PrivateRoute component={Profile} path='/profile' exact />

            <PublicRoute component={() => (<h1>not found</h1>)} path='*' />
            <PrivateRoute component={() => (<h1>not found</h1>)} path='*' />
        </Switch>
    </Router>
);

公共路由.js

const PublicRoute = ({ component: Component, ...rest }) => {
    const { user, isLoading } = useContext(AuthContext);

    if (isLoading) {
        return (
            <h1>Loading...</h1>
        );
    }

    return (
        <Route {...rest} render={(props) => (user ? <Redirect to='/dashboard' /> : <Component {...props} />)} />
    );
};

PrivateRoute.js

const PrivateRoute = ({ component: Component, ...rest }) => {
    const { user, isLoading } = useContext(AuthContext);

    if (isLoading) {
        return (
            <h1>Loading...</h1>
        );
    }

    return (
        <Route
            {...rest}
            render={(props) => (user ? (
                <AppLayout>
                    <Component {...props} />
                </AppLayout>
            ) : <Redirect to='/login' />)}
        />
    );
};

AuthStore.js

這是我管理身份驗證上下文的地方。

const AuthStore = ({ children }) => {
    const [token, setToken] = useState();
    const [user, setUser] = useState();
    const [isLoading, setIsLoading] = useState(false);

    const setUserToken = async (userToken) => {
        localStorage.setItem('token', userToken);
        setToken(userToken);
        try {
            const decodedToken = jwtDecode(userToken);
            const currentUserData = await AuthService.getUserData(decodedToken.username);
            setUser(currentUserData);
        } catch (error) {
            console.log(error.name);
        }
    };

    useEffect(() => {
        setIsLoading(true);
        const localToken = localStorage.getItem('token');
        if (localToken) {
            setUserToken(localToken);
        }
        setIsLoading(false);
    }, []);

    return (
        <AuthContext.Provider value={{
            user, token, setUserToken, setUser, isLoading,
        }}
        >
            {children}
        </AuthContext.Provider>
    );
};

太感謝了。

編輯

我真正在尋找什么: 未經授權時:

  • 搜索/dashboard時,重定向到/login
  • 搜索不存在的路由時,重定向到/login
  • 當搜索另一個未經授權的路線時,例如/register ,重定向到它就好✅

授權時:

  • 搜索/login時,重定向到/dashboard
  • 搜索不存在的路由時,重定向到 404 頁面,最好是最后兩條路由之一,看起來像<PrivateRoute component={() => (<h1>not found</h1>)} path='*'
  • 當搜索另一條授權路由時,例如/profile ,重定向到它很好 ❌ ➡️ 它當前正在重新加載路由,顯示登錄頁面,半秒后顯示/dashboard ,但不顯示/profile

根據React Router文檔, <Switch>組件的直接子級只能是 react-router-dom package 中的<Route><Redirect>組件。

All children of a <Switch> should be <Route> or <Redirect> elements. Only the first child to match the current location will be rendered.

您的<AppLayout />組件是問題所在。 <Switch>中刪除它並將其移動到您的身份驗證頁面中。

編輯

每當您在瀏覽器中輸入新地址並按 Enter 鍵時,您的身份驗證提供程序都會進入異步流程,這意味着每次您的應用程序中都會有一個 state 且用戶未登錄,那么使用此 state 的組件就會做出反應到你的改變。 同樣,您應該await setUserToken 因為在異步 function 解析之前加載設置為 false。

最后一件事在您的列表中不起作用是因為您這樣編碼;),如果有user ,您的公共路由會將它收到的所有呼叫重定向到/dashboard ,無論它應該是 404 路由還是現有路由。 我會建議不同的方法。 創建一個配置,在其中聲明路由是否受保護,然后只有一個 AuthRoute 組件將獲取路由的所有道具,並基於上下文中用戶的這個新標志和 state,完成工作

const routes = [{ name: 'home', path: '/', exact: true, isProtected: false, component: Home }, { name: 'protected', path: '/dashboard', exact: true, isProtected: true, component: Dashboard }];

const AuthRoute = ({ name, path, exact, isProtected, component: Component }) => {
    const { user } = useContext(userContext);
    if (isProtected && !user) { // redirect to login }
    if (!isProtected || (isProtected && user)) { // render route with props }

}

///...

<Switch>
   {routes.map(route => <AuthRoute {...route} />
</Switch>

就在 3 到 4 天前,我遇到了同樣的問題,當時一個非常善良的人非常了不起,在 discord 的 reactiflux 服務器上幫助了我。 您可能正在嘗試 go 到一條受保護的路線,這會起作用,但另一條受保護的路線不起作用? 您所要做的就是在一個單獨的文件中創建一個高階組件 (HOC),您可以調用protectedRoute.js並在此文件中:

import React, { useState, useEffect } from "react";
import { Redirect } from "react-router-dom";

export function protectedRoute(Component) {
  return function ComponentCheck(props) {
    const [check, setCheck] = useState(false);
    const [currentUser, setCurrentUser] = useState(false);

    useEffect(() => {
      const user = false;
      setCurrentUser(user);
      setCheck(true);
    }, []);

    const RedirectToLogin = <Redirect to="/" />;

    return check && (currentUser ? <Component {...props} /> : RedirectToLogin);
  };
}

在上面的文件中,'/' 路徑在我的例子中是登錄頁面,但是你可以說const RedirectToLogin = <Redirect to='/login' />而不是const RedirectToLogin = <Redirect to='/' /> '/' />

然后你可以限制一個路由,例如我希望/users路由只有在user === true時才能訪問。 因此,我可以在users.js文件中說:

import { protectedRoute } from "./protectedRoute";

const Users = () => {
  return <h2>Users</h2>;
};

export default protectedRoute(Users);

然后我還希望/dashboard路由受到保護:

import { protectedRoute } from "./protectedRoute";

const Dashboard = () => {
  return <h2>Dashboard</h2>;
};

export default protectedRoute(Dashboard);

但我不希望/home路由受到保護,所以:


const Home = () => {
  return <h2>Home</h2>;
};

export default Home;

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM