React-redux-universal-hot-example: How to redirect to requested URL after authorization - and with SSR?

Created on 30 Nov 2015  路  6Comments  路  Source: erikras/react-redux-universal-hot-example

I'm trying to solve the problem of redirecting the user to their originally requested URL for pages that require authorization.

Right now in this repo, we always just redirect to /loginSuccess

react-router has a great way of doing this, per this answer on SO which is to use replaceState and set nextPathname.

I have a working implementation of that here (no PR yet). However it doesn't work with Server-Side Rendering.

On the server-side, we do a hard redirect (i.e a 302) if we detect that the router wants to change the route. This discards our router.location.state.nextPathname state.

I'm not sure what the best way to preserve that state is. The next best solution I could find is to store that state as a query parameter e.g. /login?next=/original/url - an example of which I've made in this commit.

My question is, is this the correct way of doing it, or is there some secret sauce I'm missing?

Most helpful comment

I accomplished this with the following 2 snippits:

The onEnter hook

export function requireLogin(store) {
  return (nextState, replace, cb) => {
    function checkAuth() {
      const { auth: { user } } = store.getState();
      const { pathname, search } = nextState.location;
      const next = `${pathname}${search}`;
      if (!user) {
        replace(`/login?next=${encodeURIComponent(next)}`);
      }
      cb();
    }

    if (!isLoaded(store.getState())) {
      store.dispatch(load()).then(checkAuth);
    } else {
      checkAuth();
    }
  };
}

In App.js

componentWillReceiveProps(nextProps) {
    const { location: { query }, replaceRoute } = this.props;

    if (!this.props.currentAdmin && nextProps.currentAdmin) {
      // login
      replaceRoute(query.next || "/");
    } else if (this.props.currentAdmin && !nextProps.currentAdmin) {
      // logout
      replaceRoute("/login");
    }
  }

All 6 comments

Stashing the destination URL in params is a sensible solution. You could also store it server-side in session data. This hasn't been done in this repo, but having a good example would be nice.

I assumed this would work per Redux router's documentations:
function someAction () { return dispatch => { dispatch(someOtherAction); //this modifies the state. dispatch(pushState('newroute'); } }

But that didn't work in this framework for some reason.

I accomplished this with the following 2 snippits:

The onEnter hook

export function requireLogin(store) {
  return (nextState, replace, cb) => {
    function checkAuth() {
      const { auth: { user } } = store.getState();
      const { pathname, search } = nextState.location;
      const next = `${pathname}${search}`;
      if (!user) {
        replace(`/login?next=${encodeURIComponent(next)}`);
      }
      cb();
    }

    if (!isLoaded(store.getState())) {
      store.dispatch(load()).then(checkAuth);
    } else {
      checkAuth();
    }
  };
}

In App.js

componentWillReceiveProps(nextProps) {
    const { location: { query }, replaceRoute } = this.props;

    if (!this.props.currentAdmin && nextProps.currentAdmin) {
      // login
      replaceRoute(query.next || "/");
    } else if (this.props.currentAdmin && !nextProps.currentAdmin) {
      // logout
      replaceRoute("/login");
    }
  }

@jaraquistain thanks. Those snippet worked for me as well in this boiler plate.

But just come across as a little inelegant, seems it requires you to check for changed state else where.
What if the previous action doesn't change the state in anyway. I.e. just after dispatching that particular action, the user should be redirect elsewhere. Yes, I can work around it by always require the previous action change the state in someway so that on ComponentWillReceiveProps I can detect that, but still seems very hacky.

Seems dispatch(pushState) or dispatch(replaceState) should just work in the action itself.

@jaraquistain this looks cool. did you try to pass your next url by using react-router's replace method? (here's an example: https://github.com/reactjs/react-router/blob/master/examples/auth-flow/app.js#L118-L125). I tried it but this.props.location.state appears to be null.

@altayaydemir I do use replace in both my requireLogin hood and my App.js logic. I'm not sure that this.props.location.state is something that is available in the location object. Are you sure you didn't mean just this.props.location? As in my example, I access this.props.location.query in App.js and both nextState.location.pathname and nextState.location.search in requireLogin. this.props.location should contain everything you need:

requireLogin.js (see above for full snippet)

const { pathname, search } = nextState.location;

App.js (see above for full snippet)

const { location: { query }, replaceRoute } = this.props;
Was this page helpful?
0 / 5 - 0 ratings