在登录重定向之前强制ReactJS路由索引重新评估

问题描述 投票:0回答:1

我希望根据用户本地存储中是否存在令牌,有条件地将经过身份验证的路由包含在我的 ReactJS 应用程序的路由索引中。但是,要实现此目的,必须在重定向之前在登录时重新评估路由。

以下是理想的使用场景:

当用户登录时(由

handleLogin
函数处理),浏览器的本地存储中会设置一个令牌,然后用户将被重定向到
/dashboard

router
中的三元运算符有条件地在接受的路由中包含私有页面,例如,旨在让经过身份验证的用户进入其仪表板。

问题:

在设置令牌和用户重定向之间,路由似乎没有更新。这意味着,用户登录后会遇到错误,因为仪表板还不是有效路径(即使应该是有效路径)。

相关代码片段:

LoginPage.jsx

  const handleLogin = () => {
    console.log(
      `Sending login request to ${process.env.REACT_APP_API_URL}/api/auth`,
    );
    fetch(`${process.env.REACT_APP_API_URL}/api/auth`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ email, password }),
    })
      .then((response) => response.json())
      .then((data) => {
        if (data.message === "success") {
          // the login was successful
          setToken(data.token);
          console.log("successful login");
          navigate("/dashboard");
          // window.alert("The login was successful");
          // get value of token using useAuth
        } else {
          window.alert(
            `The login failed with the following error:\n\n${data.error}`,
          );
        }
      })
      .catch((error) => {
        console.error("Could not login: ", error);
        window.alert("An error occurred while trying to login.");
      });
  };

index.jsx

const Routes = () => {
    const { token } = useAuth();
    const [ isAuthenticated, setIsAuthenticated ] = useState(!!token);

    useEffect(() => {
        setIsAuthenticated(!!token);
    }, [token])

    // route configurations go here

    const routesAuthenticated = [
        {
            path: "/",
            element: <ProtectedRoute />,
            children: [
                {
                    path: "/",
                    element: <UserDashboardPage />
                },
                {
                    path: "/dashboard",
                    element: <UserDashboardPage />
                },
                {
                    path: "/logout",
                    element: <LogoutPage />
                },
            ]
        }
    ];

    const routesUnauthenticated = [
        {
            path: "/",
            element: <LandingPage />
        },
        {
            path: "/login",
            element: <LoginPage />
        },
        {
            path: "/about",
            element: <AboutPage />
        },
        {
            path: "/ipsum",
            element: <IpsumPage />
        }
    ];

    // decide which routes are available to user based on authentication status
    const router = createBrowserRouter([
        // we use the ... operator to combine these arrays into one
        ...routesUnauthenticated,
        ...(isAuthenticated ? routesAuthenticated : [])
    ]);

    // provide configuration using RouterProvider
    return <RouterProvider router={router} />;
};

尝试修复:

我尝试使用以下代码强制登录时重新加载路由

useEffect(() => {
    setIsAuthenticated(!!token);
}, [token])

不幸的是,这并不能保证在

handleLogin
将用户重定向到其仪表板之前重新加载路线。

感谢您的宝贵时间。感谢所有帮助!

reactjs react-router jsx react-router-dom
1个回答
0
投票

基本问题是您正在有条件地渲染路线,并且正如您所发现的,当您尝试导航到目标路线时,目标路线尚未安装且无法匹配。解决方案是**无条件渲染所有路由并相应地保护它们。

建议:

  • 更新
    ProtectedRoute
    以确保仅允许经过身份验证的用户访问,否则重定向到安全且不受保护的路线。
  • 创建
    AnonymousRoute
    组件以确保仅允许未经身份验证的用户访问,否则重定向到任何安全的非匿名路由。
  • 创建一个包装器组件,根据当前用户身份验证呈现一个或另一个组件。
const ProtectedRoute = () => {
  const { token } = useAuth();
  const location = useLocation();

  return !!token
    ? <Outlet />
    : <Navigate to="/login" replace state={{ from: location }} />;
};
const AnonymousRoute = () => {
  const { token } = useAuth();

  return !!token
    ? <Navigate to="/" replace />
    : <Outlet />;
};
const Wrapper = ({
  authenticatedElement = null,
  unauthenticatedElement = null,
}) => {
  const { token } = useAuth();

  return !!token ? authenticatedElement : unauthenticatedElement
}
const router = createBrowserRouter([
  {
    path: "/",
    element: (
      <Wrapper
        authentictedElement={<UserDashboardPage />}
        unauthentictedElement={<LandingPage />}
      />
    ),
  },
  {
    element: <ProtectedRoute />,
    children: [
      {
        path: "/dashboard",
        element: <UserDashboardPage />
      },
      {
        path: "/logout",
        element: <LogoutPage />
      },
      // ... other protected authenticated routes ...
    ]
  },
  {
    element: <AnonymousRoute />,
    children: [
      {
        path: "/login",
        element: <LoginPage />
      },
      // ... other protected unauthenticated routes ...
    ]
  },
  {
    path: "/about",
    element: <AboutPage />
  },
  {
    path: "/ipsum",
    element: <IpsumPage />
  },
  // ... other unprotected routes ...
]);

const Routes = () => {
  return <RouterProvider router={router} />;
};
© www.soinside.com 2019 - 2024. All rights reserved.