简体   繁体   中英

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

I am new to react and I am sure there is an explanation I do not understand, but I cannot find an answer...

I Want to app if user is logged in,so I followed some guide and created HOC(PrivateRoute) but for some reason I keep ending up in the login page of the app,

Even if localStorage contains a valid JWT and I am asking for the specific path(/wallet/portfolio), it is still redirecting me to login page, can someone please help me understand the issue(I am using typescript but I am sure it is unrelated to the issue)

My Main Component(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(copied from a guide on the web...)

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(My Context provider):

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

and finally the (JWTToken.tsx -> just for interfaces)


// 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 helped me understand the issue, because at first the user is not authenticated(until JWT is authed by the server -> async) we need to add the following in the react dom Switch:

<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>

of course the downside is that if user is logged in he will not be able to go to any public page(like registration), but I can live with that...

thanks @Tholle

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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