简体   繁体   English

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

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

I need to make a router system that distinguishes between authenticated and non-authenticated users.我需要制作一个区分经过身份验证和未经身份验证的用户的路由器系统。

What I want to achieve with this is that authed users can see their dashboard and personal details, but unauthed users can only see the landing page, the login form and the register form.我想要实现的是,经过身份验证的用户可以看到他们的仪表板和个人详细信息,但未经身份验证的用户只能看到登录页面、登录表单和注册表单。

I currently have context management so I know whether a user is logged in or not.我目前有上下文管理,所以我知道用户是否登录。 I just need to figure out all the redirects and routing related things.我只需要弄清楚所有重定向和路由相关的事情。

My code is making all sorts of "strange" things.我的代码正在制作各种“奇怪”的东西。 I don't really know where I have messed it up.我真的不知道我在哪里搞砸了。

AppContent.js应用内容.js

This is where all the routing happens.这是所有路由发生的地方。 AppLayout is just a layout wrapper with a header and a footer that must be visible only when logged in. 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>
);

PublicRoute.js公共路由.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 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 AuthStore.js

This is where I manage my auth context.这是我管理身份验证上下文的地方。

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

Thank you so much.太感谢了。

EDIT编辑

What I am really looking for: When Unauthorized:我真正在寻找什么: 未经授权时:

  • When searching for /dashboard , redirect to /login搜索/dashboard时,重定向到/login
  • When searching for non existing route, redirect to /login搜索不存在的路由时,重定向到/login
  • When searching for another unauthorized route like /register , redirect to it fine ✅当搜索另一个未经授权的路线时,例如/register ,重定向到它就好✅

When Authorized:授权时:

  • When searching for /login , redirect to /dashboard搜索/login时,重定向到/dashboard
  • When searching for non existing route, redirect to a 404 page, preferably one of the two last routes, the ones that look like <PrivateRoute component={() => (<h1>not found</h1>)} path='*'搜索不存在的路由时,重定向到 404 页面,最好是最后两条路由之一,看起来像<PrivateRoute component={() => (<h1>not found</h1>)} path='*'
  • When searching for another authorized route like /profile , redirect to it fine ❌ ➡️ It is currently reloading the routes, showing the login page and after half a second it shows /dashboard , but not /profile当搜索另一条授权路由时,例如/profile ,重定向到它很好 ❌ ➡️ 它当前正在重新加载路由,显示登录页面,半秒后显示/dashboard ,但不显示/profile

According to React Router documentation, direct children of <Switch> component can only be <Route> or <Redirect> components from react-router-dom package.根据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.

Your <AppLayout /> component is the problem.您的<AppLayout />组件是问题所在。 Remove it from <Switch> and move it inside your auth pages.<Switch>中删除它并将其移动到您的身份验证页面中。

edit编辑

your auth provider is going trough async flow anytime you type in a new address in the browser and press enter, meaning every time you will have a state in your app where user is not logged in, then the components that are consuming this state are reacting to your change.每当您在浏览器中输入新地址并按 Enter 键时,您的身份验证提供程序都会进入异步流程,这意味着每次您的应用程序中都会有一个 state 且用户未登录,那么使用此 state 的组件就会做出反应到你的改变。 also in the effect you should await setUserToken because loading is set to false before that async function resolves.同样,您应该await setUserToken 因为在异步 function 解析之前加载设置为 false。

the last thing that is not working on your list is because you coded it like that;), your public route redirects all calls it receives to the /dashboard if there is user , regardless if it should be a 404 route or existing route.最后一件事在您的列表中不起作用是因为您这样编码;),如果有user ,您的公共路由会将它收到的所有呼叫重定向到/dashboard ,无论它应该是 404 路由还是现有路由。 i would suggest different approach.我会建议不同的方法。 create a config where you declare if route is protected or not, then have only one AuthRoute component that will take all the props of the route, and based on this new flag and state of user in the context, do the work创建一个配置,在其中声明路由是否受保护,然后只有一个 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>

I was facing the same problems just 3 to 4 days back when a very kind person who is just amazing, helped me out on the reactiflux server of discord.就在 3 到 4 天前,我遇到了同样的问题,当时一个非常善良的人非常了不起,在 discord 的 reactiflux 服务器上帮助了我。 You might be trying to go to one protected route and that would work but the other protected route will not work right??您可能正在尝试 go 到一条受保护的路线,这会起作用,但另一条受保护的路线不起作用? All you have to do is create a Higher Order Component (HOC) in a separate file which you can maybe call protectedRoute.js and inside this file:您所要做的就是在一个单独的文件中创建一个高阶组件 (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);
  };
}

In the above file the '/' route in my case is the login page but instead of const RedirectToLogin = <Redirect to='/' /> you can say const RedirectToLogin = <Redirect to='/login' />在上面的文件中,'/' 路径在我的例子中是登录页面,但是你可以说const RedirectToLogin = <Redirect to='/login' />而不是const RedirectToLogin = <Redirect to='/' /> '/' />

and then you can restrict a route like for example I want the /users route to be accessible only if user === true .然后你可以限制一个路由,例如我希望/users路由只有在user === true时才能访问。 So for that i can in the users.js file say:因此,我可以在users.js文件中说:

import { protectedRoute } from "./protectedRoute";

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

export default protectedRoute(Users);

and then I also want the /dashboard route to be protected so:然后我还希望/dashboard路由受到保护:

import { protectedRoute } from "./protectedRoute";

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

export default protectedRoute(Dashboard);

but I dont want the /home route to be protected so:但我不希望/home路由受到保护,所以:


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

export default Home;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM