5

Right now, my router looks like this:

<Router history={browserHistory}>
    <Route component={Provider}>
        <Route path="/" component={AppPage}>
            <Route path="login" component={LoginPage}/>
            <Route component={LoggedinPage}>
                <Route path="onboarding" component={OnboardingPage}/>
                <Route component={OnboardedPage}>
                    <IndexRoute component={HomePage}/>
                    <Route path="settings" component={SettingsPage}/>
                </Route>
            </Route>
        </Route>
    </Route>
</Router>

LoggedinPage redirects to /login if the user isn't logged in and OnboardedPage redirects to /onboarding if the user hasn't completed the onboarding flow (choosing username, etc). However, I think more of these conditional redirects may be needed in the future. What's the best way to handle these conditional redirects? Is it possible to have a single component that handles all the redirects?

Leo Jiang
  • 24,497
  • 49
  • 154
  • 284
  • You shouldn't store jwt tokens inside of localStorage. https://stackoverflow.com/questions/44133536/is-it-safe-to-store-a-jwt-in-localstorage-with-reactjs – Justin Feb 19 '22 at 01:27

2 Answers2

1

<Route>s accept an onEnter hook that is called when the route matches. A hook would look something like this:

function requireAuth(nextState, replace) {
    if (!loggedIn()) {
        replace({ pathname: 'login' });
    }
}

Then use it like so:

<Route path="/" component={AppPage}>
    <Route path="home" component={HomePage} onEnter={requireAuth}/>
    <Route path="login" component={LoginPage}/>
</Route>

A more composable example, that lets you combine multiple checks:

function checkAuth() {
    if (!loggedIn()) {
        return { pathname: 'login' };
    }
}

function checkOnboarding() {
    if (!completedOnboarding()) {
        return { pathname: 'onboarding' };
    }
}

function redirect(...checks) {
    return function(nextState, replace) {
        let result;
        checks.some(check => {
            result = check();
            return !!result;
        });
        result && replace(result);
    };
}

Then combine away!

<Route path="/" component={AppPage}>
    <Route path="home" component={HomePage}
        onEnter={redirect(checkAuth, checkOnboarding)}/>
    <Route path="login" component={LoginPage}/>
    <Route path="onboarding" component={OnboardingPage}
        onEnter={redirect(checkAuth)}/>
</Route>
David Tang
  • 92,262
  • 30
  • 167
  • 149
  • +1, That's a good approach, only thing that bothers me is that replace refreshes the entire window and this is not contained within the SPA style. – Omri Aharon Aug 09 '16 at 10:13
0

You could have a function that checks the if the user is logged in, and just import that:

import {browserLocation} from './browser-location'
export default function checkLoggedIn() {
    if (localStorage.jwt === null)
        browserLocation.pushState('/login')
}
Zane Hitchcox
  • 936
  • 1
  • 9
  • 23
  • I'll have to parse the URL manually this way right? Like I'll need `if (localStorage.jwt === null && !window.location.pathname.match(/^\/login(\/|$)/))`Is there a way to do this and still use React Router to parse the URL? – Leo Jiang Mar 29 '16 at 01:41
  • I redid my answer. So, you just import this function and call it in the render portion of your component to which you are routing. – Zane Hitchcox Mar 29 '16 at 01:55