繁体   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