How redirect to login page when got 401 error from ajax call in React-Router?

Chris picture Chris · Aug 31, 2015 · Viewed 7.5k times · Source

I am using React, React-Router and Superagent. I need authorization feature in my web application. Now, if the token is expired, I need the page redirect to login page.

I have put the ajax call functionality in a separated module and the token will be send on each request's header. In one of my component, I need fetch some data via ajax call, like below.

 componentDidMount: function() {

    api.getOne(this.props.params.id, function(err, data) {
      if (err) {
        this.setErrorMessage('System Error!');
      } else if (this.isMounted()) {
        this.setState({
          user: data
        });
      }
    }.bind(this));

  },

If I got 401 (Unauthorized) error, maybe caused by token expired or no enough privilege, the page should be redirected to login page. Right now, in my api module, I have to use window.loication="#/login" I don't think this is a good idea.

var endCallback = function(cb, err, res) {

  if (err && err.status == 401) {
    return window.location('#/login');
  }

  if (res) {
    cb(err, res.body);
  } else {
    cb(err);
  }
};

get: function(cb) {
  request
    .get(BASE_URL + resources)        
    .end(endCallback.bind(null, cb));
},

But, I can't easily, call the react-router method in my api module. Is there an elegant way to implemented this easy feature? I don't want to add an error callback in every react components which need authorized.

Answer

BradByte picture BradByte · Aug 31, 2015

I would try something like this:

  1. Use the component's context to manipulate the router (this.context.router.transitionTo()).
  2. Pass this method to the API callout as a param.

// component.js
,contextTypes: {
  router: React.PropTypes.func
},
componentDidMount: function() {
    api.getOne(this.props.params.id, this.context.router, function(err, data) {
      if (err) {
        this.setErrorMessage('System Error!');
      } else if (this.isMounted()) {
        this.setState({
          user: data
        });
      }
    }.bind(this));
},


// api.js
var endCallback = function(cb, router, err, res) {

  if (err && err.status == 401) {
    return router.transitionTo('login');
  }

  if (res) {
    cb(err, res.body);
  } else {
    cb(err);
  }
};

get: function(cb, router) {
  request
    .get(BASE_URL + resources)        
    .end(endCallback.bind(null, cb, router));
},

I know you didn't want a callback on each authenticated component, but I don't think there's any special react-router shortcuts to transition outside of the router.

The only other thing I could think of would be to spin up a brand new router on the error and manually send it to the login route. But I don't think that would necessarily work since it's outside of the initial render method.