PrivateRoute 在刷新页面时总是重定向到登录页面;如何等待useEffect()和localStorage?

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

我正在我的应用程序中实现安全和用户管理,并遇到了 React router、Context、localStorage 的问题。

使用路由器,我创建了一条私有路由,并检查 Context 或 localStorage 上是否存在用户 ID,如果用户 ID 不存在,则将用户重定向到登录页面。 这很好用,但是如果我刷新页面,它总是重定向到登录页面。即使用户已登录,并且应该存在于本地存储中。

我认为问题源于 localStorage 和“useEffect()”。 我的理论是,路由器在 localStorage (和 useEffect)完成检查 userID 是否存在之前进行重定向。 不知道如何解决这个问题?

这是我的私有路由组件的代码”

import {
    Route,
    Redirect,
    RouteProps,
} from 'react-router-dom';
import {MyContextProvider, useMyContext} from '../Context/UserContext';


interface PrivateRouteProps extends RouteProps {
    // tslint:disable-next-line:no-any
    component: any;
    UserID: number;
}

const PrivateRoute = (props: PrivateRouteProps) => {
    const { component: Component, UserID, ...rest } = props;

    console.log("Inside Route: "+ UserID);

    return (
        <Route
            {...rest}
            render={(routeProps) =>
                UserID > 0 ? (
                    <Component {...routeProps} />
                ) : (
                        <Redirect
                            to={{
                                pathname: '/',
                                state: { from: routeProps.location }
                            }}
                        />
                    )
            }
        />
    );
};

export default PrivateRoute;

^ " console.log("Inside Route: "+ UserID); " 打印 0,这就是为什么它总是重定向到登录页面。这就是为什么我认为 useEffect() (如下所示的路由)可能与 userID 设置不正确有关。

路由部分代码:

function Routing() { 
  const classes = useStyles();
  const [store, setStore] = useMyContext();

  useEffect(() => { // check if user exists in localstorage, if so, set the values on react Context
    const res = localStorage.getItem("UserID");
    if (res) {
        // since LocalStorage can return a type or null, we must check for nulls and set accordingly.
        const uID = localStorage.getItem("UserID");
        const UserID =  uID !== null ? parseInt(uID) : 0;

        const un = localStorage.getItem("Username");
        const Username = un !== null ? un : '';

        const iA = localStorage.getItem("IsAdmin");
        const isAdmin = iA != 'true' ? true : false;

        const eN = localStorage.getItem("EmailNotifications");
        const emailNotifications = eN != 'true' ? true : false;
       
        setStore({ // update the 'global' context with the logged in user data
            UserID:UserID,
            Username: Username,
            IsAdmin: isAdmin,
            EmailNotifications: emailNotifications
          });
        console.log(UserID);
        console.log(Username);
        console.log(isAdmin);
        console.log("foundUser");
    }
  }, []);

  const id = store.UserID; // get the userID from context; to pass into PrivateRoute


  console.log("ID: ++"+ id);
  return (
      <div className="App">
          <Router>
            <Switch>
              <Route exact path="/" component={Login}/>
              <PrivateRoute path="/dashboard" UserID={id} component={Dashboard}/>
              <PrivateRoute path="/add" UserID={id} component={AddTicket}/>
              <PrivateRoute path="/settings" UserID={id} component={Settings}/>
              <Route component={NotFound}/>
            </Switch>
        </Router>
    </div>
  );
}

export default Routing;
reactjs typescript react-router-dom
3个回答
0
投票

为了解决我想要完成的任务,我只是将 localStorage 检索直接添加到 PrivateRoute 中(而不是使用 useEffect 的 PrivateRoute 的父节点)

const PrivateRoute = (props: PrivateRouteProps) => {
const { component: Component, UserID, ...rest } = props;

console.log("Inside Route: "+ UserID);

return (
    <Route
        {...rest}
        render={(routeProps) =>
            localStorage.getItem("UserID")  ? ( // if UserID == 0 when user is NOT logged in. So, if ID == 0, redirect to signinpage
                <Component {...routeProps} />
            ) : (
                    <Redirect
                        to={{
                            pathname: '/',
                            state: { from: routeProps.location }
                        }}
                    />
                )
        }
    />
);

};


0
投票

将用户的 ID 存储在存储中是非常糟糕的主意。这是敏感信息。尝试实现在等待从您的服务器或您正在使用的服务器获取 id 时显示旋转器或 HTML 骨架的逻辑。 URL 可能会稍微改变,但用户只会看到微调器/HTML 骨架,这比重定向和显示用户不应该看到的页面更好的用户体验,更不用说用户的 id 保存在

LocalStorage
中。


0
投票
import { useSelector } from 'react-redux'
import { Navigate, Outlet } from 'react-router'
import { selectCurrentToken } from '../features/auth/authSlice'

function PrivateRoutes() {
  const token = useSelector(selectCurrentToken) || localStorage.getItem('token')

  return token ? <Outlet /> : <Navigate to='/login' />
}

export default PrivateRoutes

通过将 localStorage.getItem('token') 与 useSelector(selectCurrentToken) 一起使用,我确保即使在页面刷新后令牌仍然存在,从而防止意外重定向。

© www.soinside.com 2019 - 2024. All rights reserved.