Matt Fordham Matt Fordham - 2 months ago 35
React JSX Question

Possible to delay component rendering with `onEnter`?

I am using the React Router

onEnter
attribute to handle authentication, like below, and also as demonstrated in the "auth-flow" example.

function requireAuth(nextState, replace, next) {
Auth.validateToken()
.then(() => {
next();
})
.fail(() => {
replace('/login');
next();
});
}

<Router history={history}>
<Route path="/" component={App} >
<IndexRoute
component={Dashboard}
onEnter={requireAuth}
/>
<Route
path="login"
component={LogInForm}
/>
</Route>
</Router>


I am using the asynchronous callback functionality of
onEnter
because my token validation requires a server call.

The problem I am experiencing is that the
Dashboard
component in this example renders/mounts before
next()
is called. Ideally, rendering would wait until the callback is fired.

Am I misunderstanding how this is intended to work? Is there a way to delay rendering of route components until certain conditions are true?

Answer

Don't ever delay route rendering, your users deserve better.

There is absolutely a different way to do this, and that's by having the actual page you render contain the logic for seeing if someone's authorized to view it, with a starting state that has soemthing like authorized: false, and that your callback switches using this.setState({ authorized: resultFromCallback }).

That state change will cause React to call render() again on your page, if the value was different, and then in your page's render() function you simply check this.state.authorized to see whether you should present the user with a UI that contains all the bits they should see as anonymous user, or all the bits they should see as authorized user.

...
  componentDidount() {
    whateverlib.checkForAuthorization(this.handleAuthorization);
  },
  handleAuthorization(authorized) {
    this.setState({ authorized });
  },
  render() {
    if(this.state.authorized) {
      return this.renderAuthorizedUI();
    }
    return this.renderAnonymousUI();
  },
...

Also remember that your app is still just JavaScript running on a webpage, so why would you even check for every page you load? All you need to do is check on any page, store that result, and done. As long as the user keeps their tab open, their authorization state does not spontaneously change from one react-router app-managed page to the next, so you can use an app-global tracker.

var authorizer = require('./lib/whatever-thing-you-wrote-for-this');

var YourPage = React.createClass({
  ...
  componentDidMount() {
    if (!authorizer.isAuthorized()) {
      authorizer.tryAuthorize(this.handleAuthorization);
    }
  },
  handleAuthorization() {
    this.forceUpdate();
  },
  render() {
    if(authorizer.isAuthorized()) {
      return this.renderAuthorizedUI();
    }
    return this.renderAnonymousUI();
  },
  ...
});
Comments