【问题标题】:Restricted navigation React Router Dom - Private and Public routes受限导航 React Router Dom - 私有和公共路由
【发布时间】:2021-04-12 14:50:53
【问题描述】:

我需要做一个路由器系统来区分认证用户和非认证用户。

我想要实现的是,经过身份验证的用户可以看到他们的仪表板和个人详细信息,但未经身份验证的用户只能看到登录页面、登录表单和注册表单。

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

我的代码正在制作各种“奇怪”的东西。我真的不知道我在哪里搞砸了。

AppContent.js

这是所有路由发生的地方。 AppLayout 只是一个带有页眉和页脚的布局包装器,只有在登录时才可见。

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

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 页面,最好是最后两条路线之一,看起来像 &lt;PrivateRoute component={() =&gt; (&lt;h1&gt;not found&lt;/h1&gt;)} path='*'
  • 当搜索另一个授权路由时,如/profile,重定向到它很好❌➡️它正在重新加载路由,显示登录页面,半秒后显示/dashboard,但不是/profile

【问题讨论】:

  • 你能描述一下发生了什么奇怪的事情吗?我也不是 100% 确定,但我不认为 switch 将与不是路由或重定向的直接子代一起使用,你能删除布局组件并查看它是否已修复?

标签: javascript reactjs routes react-router-dom


【解决方案1】:

根据React Router 文档,&lt;Switch&gt; 组件的直接子级只能是 react-router-dom 包中的 &lt;Route&gt;&lt;Redirect&gt; 组件。

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

您的&lt;AppLayout /&gt; 组件是问题所在。将其从 &lt;Switch&gt; 中删除并将其移至您的身份验证页面中。

编辑

每当您在浏览器中输入新地址并按 Enter 键时,您的身份验证提供程序都会进入异步流程,这意味着每次您的应用程序中都会出现用户未登录的状态,那么正在使用此地址的组件状态正在对您的更改做出反应。同样,您应该await setUserToken,因为在异步函数解析之前加载设置为 false。

最后没有在您的列表上工作的事情是因为您这样编码;),如果有 user,您的公共路由会将它收到的所有呼叫重定向到 /dashboard,无论它是否应该是 404路线或现有路线。我会建议不同的方法。创建一个配置,在其中声明路由是否受保护,然后只有一个 AuthRoute 组件将获取路由的所有道具,并根据上下文中的这个新标志和用户状态,完成工作

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>

【讨论】:

  • 我已经这样做了,现在它有点工作,这已经修复了一个错误,即如果在未经授权的情况下浏览不存在的路由时重定向到私有路由,而不是重定向到 404 路由。但是,现在当我搜索 /demo 时获得授权,它会将我重定向到 /dashboard。未经授权时不会发生这种情况,如果我搜索 /register 它会很好地重定向我。
  • 这部分是由最后两条路线引起的,即“未找到”路线,如果我更改它们的顺序,应用程序的行为方式会有所不同。现在我想实现这个: - 未授权时:[搜索 /dashboard 时,重定向到 /login],[搜索不存在的路由时,重定向到 /login],[搜索另一个未经授权的路由时,重定向到它fine] // - 授权时:[搜索 /login 时,重定向到 /dashboard],[搜索不存在的路由时,重定向到 PrivateRoute path='*'],[搜索另一个授权路由时,重定向到它好的]。
  • 请编辑您的问题,准确描述正在发生的事情以及您希望发生的事情
  • 完成,我添加了一个要求我的路由完全正常的要求列表,并使用您之前告诉我的关于 的修复程序编辑了 de JavaScript
  • 当您说“搜索路线”时,您的意思是您在地址栏中输入路径并按回车键?应用重新加载?
【解决方案2】:

就在 3 到 4 天前,我遇到了同样的问题,当时一个非常友善的人非常了不起,在 discord 的 reactiflux 服务器上帮助了我。 您可能正试图去一个受保护的路线,这会奏效,但另一条受保护的路线将无法正常工作? 您所要做的就是在一个单独的文件中创建一个高阶组件 (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 = &lt;Redirect to='/login' /&gt;而不是const RedirectToLogin = &lt;Redirect to='/login' /&gt;

然后你可以限制一个路由,例如我希望/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;

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-21
    • 2020-08-13
    • 1970-01-01
    • 2018-01-28
    • 2022-01-08
    • 2018-06-08
    • 1970-01-01
    • 2022-11-27
    相关资源
    最近更新 更多