react-router-dom with react hooks 不重定向到正确的路由

react-router-dom with react hooks Does not redirect to correct route

我是新手,我确定有一个解释我不明白,但我找不到答案...

我想在用户登录时使用应用程序,所以我遵循了一些指南并创建了 HOC(PrivateRoute) 但由于某种原因我一直在应用程序的登录页面结束,

即使 localStorage 包含有效的 JWT 并且我要求提供特定路径 (/wallet/portfolio),它仍将我重定向到登录页面, 有人可以帮我理解这个问题吗(我正在使用打字稿,但我确信它与这个问题无关)

我的主要组成部分(App.tsx)

//imports...

export const App = () => {
    const[jWTContext, setJWTContext] = useState(jWTContextInitialState);
    const[isLoggedIn, setIsLoggedIn] = useState(false);

    useEffect(() => {
        if (!isLoggedIn) {
            jWTContext.isValid().then(loggedIn => {
                    setIsLoggedIn(loggedIn);
                }
            ).catch(() => {setIsLoggedIn(false)});
        }
    });
    return (
        <JWTContext.Provider value={jWTContext}>
            <BrowserRouter>
                <Switch>
                    <PrivateRoute exact path="/wallet/portfolio"  component={AppPortfolio}  isSignedIn={isLoggedIn}/>
                    <Route exact path="/wallet/login" component={AppLogin} />
                    <Route exact path="/wallet/register" component={AppRegister} />
                    <Redirect to={isLoggedIn ? "/wallet/portfolio" : "/wallet/login"}/>
                </Switch>
            </BrowserRouter>
        </JWTContext.Provider>
    );
};

PrivateRoute.tsx(从网络上的指南复制...)

import * as React from 'react';
import {Route, Redirect, RouteProps} from 'react-router-dom';

interface PrivateRouteProps extends RouteProps {
    component: any;
    isSignedIn: boolean;
}

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

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

export default PrivateRoute;

JWTContext.tsx(我的上下文提供者):

import React from 'react';
import {JWTBody, JWTHeader, JWTToken} from "../interfaces/JWTToken"

export interface JWTContext {
    jWTToken: JWTToken | null;
    setJWTToken(jWTToken: JWTToken | null): void;
    isValid(): Promise<boolean>;
    updateFromLocalStorage(): (JWTToken | null);
}

export const jWTContextInitialState : JWTContext = {
    jWTToken: initFromLocalStorage(),

    setJWTToken: (jWTToken) => {
        jWTContextInitialState.jWTToken = jWTToken;
    },

    isValid: async() => {
        if (jWTContextInitialState.jWTToken) {
            let response = await fetch("/auth/tokenvalid",{
                method: "GET",
                headers: {
                    "Content-Type": "application/json",
                    "Authorization": JSON.stringify(jWTContextInitialState.jWTToken)}});
            let res = await response.json();
            if (res.code === 200)
                return true;
        }
        return false;
    },

    updateFromLocalStorage: () => {
        return initFromLocalStorage()
    }
};

function initFromLocalStorage() {
    let localStorageToken = localStorage.getItem("velonaJWT");
    if (localStorageToken) {
        return parseJwt(localStorageToken);
    }
    return null;
}


function parseJwt (token: string): JWTToken {
    token = token.replace("Bearer ", "");
    let tokenArray = token.split('.');

    let header = tokenArray[0];
    header = header.replace('-', '+').replace('_', '/');
    let bufferHeader = Buffer.from(header, "base64");
    let dataHeader: JWTHeader =  JSON.parse(bufferHeader.toString());

    let body = tokenArray[1];
    body = body.replace('-', '+').replace('_', '/');
    let bufferBody = Buffer.from(body, "base64");
    let dataBody: JWTBody =  JSON.parse(bufferBody.toString());

    return ({
        header: dataHeader,
        body: dataBody,
        sig: tokenArray[2]
    });
    //return  JWTToken(JSON.parse(bufferHeader.toString()), JSON.parse(bufferBody.toString()), tokenArray[2])
}


export const JWTContext = React.createContext(jWTContextInitialState);

最后是(JWTToken.tsx -> 仅用于接口)


// represent velona WJT token
export interface JWTToken {
    header: JWTHeader,
    body: JWTBody,
    sig: string,
}

export interface JWTHeader {
    typ: string,
    alg: string
}

export interface JWTBody {
    sub: number,
    jti: string,
    authorities: Authorities[],
    iat: number,
    nbf: number,
    exp: number,
    environment: string
}

export enum Authorities {
    ROLE_USER,
    ROLE_TRUSTEE,
    ROLE_EMPLOYEE,
    ROLE_ADMIN
}

@Tholle 帮助我理解了这个问题, 因为起初用户未通过身份验证(直到 JWT 被服务器授权 -> 异步)我们需要在 react dom 开关中添加以下内容:

<BrowserRouter>
    <Switch>
        <PrivateRoute exact path="/wallet/portfolio"  component={AppPortfolio}  isSignedIn={isLoggedIn}/>
                    /*all other private routes here*/
        { isLoggedIn ? <Redirect to="/wallet/portfolio"/> : "" }
        <Route exact path="/wallet/login" component={AppLogin} />
        <Route exact path="/wallet/register" component={AppRegister} />
        <Redirect to={isLoggedIn ? "/wallet/portfolio" : "/wallet/login"}/>
    </Switch>
</BrowserRouter>

当然缺点是,如果用户登录,他将无法进入任何 public 页面(如注册),但我可以接受...

感谢@Tholle