SSR, Flow Router - am I redirecting wrong?

I’ve got this logic in place:

let userDashboard = FlowRouter.group({
  prefix: '/user/dashboard',
  name: 'userArea',
  triggersEnter: [function (context, redirect) {
    if (!Meteor.user()) {
      FlowRouter.go('/user/login');
    }
  }]
});

If I am not logged in and go to /user/dashboard, I get bounced to /user/login but with a warning:

Warning: React attempted to reuse markup in a container but the checksum was invalid. This generally means that you are using server rendering and the markup generated on the server was not what the client was expecting. React injected new markup to compensate which works but you have lost many of the benefits of server rendering. Instead, figure out why the markup being generated is different on the client or server:
(client) <div class=“container” data-reac
(server) <div class=“user-layout” data-

Am I doing something wrong? My guess is that since this is on both client and server, there’s a brief moment of them being out of sync.

Here’s what’s happening:

  1. You are requesting a template from /user/dashboard and the server sends that as the first response
  2. Then the client router logic runs and figures out that that’s not the right template because you are not logged in
  3. The template from /user/login gets rendered

What you should do anyway is get that authentication logic out your router. Authentication is dependent on a reactive method but flow router explicitly frowns upon reactivity in the router layer.

Take a look at these:

2 Likes

Ahh, Serkan delivers as usual! Thanks. :smile:

So it’s clear that I need to move the auth checking out of the router. But I still want to be able to redirect. In other words, what I don’t want is this:

User goes to /user/dashboard -> is presented with a login form at that URL

What I do want:

User goes to /user/dashboard -> is redirected to /user/login

The reason for this is that I want to maintain compatibility with automatic form fillers like 1Password and Dashlane, which remember a specific URL in order to auto-login.

I’m not really sure how to go about this. I have a UserLayout component which is basically the shell for all the password-protected user pages, and this does not work:

UserLayout = React.createClass({
  displayName: 'UserLayout',

  componentWillMount() {
    if (!Meteor.userId()) {
      FlowRouter.go('/user/login');
    }
  },

I still get the client/server mismatch warning. I get the part about Accounts.onLogin, but what about redirecting when the user is trying to access a protected page?

why you are not using redirect as in flow-router documentation instead of FlowRouter.go ?

1 Like

No problem :smile:

I guess you could avoid the discrepancy by implementing both strategies together, so that

  1. the template from user/dashboard displays the loginForm template
  2. you still redirect to user/login which also displays the same loginForm template

Also @shock’s suggestion to use the built-in redirect function is a good idea anyway.

Good point! Totally forgot about that even though it was right in front of me. :slight_smile: So I tried this:

  triggersEnter: [function (context, redirect) {
    if (!Meteor.userId()) {
      redirect('userLogin');
    }
  }]

But I still get the warning. I guess my options are:

  1. Forget SSR and go with a traditional client-side solution. The main reason I’m interested in SSR is to make SEO a lot more straightforward, but SSR has been a bit troublesome (on another project as well).
  2. Just render the login/signup form on whatever restricted page the user hits (dashboard, account settings, etc).

I think the way FR suggest to not to redirect, but to render something on the same layout (without changing the route).
We build this with a reason, and I hope we’ve covered it in the Routing Guide (and check some Meteor forum links in that).

If you do that, SSR works pretty well.
We’ve not implemented a solution for redirecting in server side yet. Still, figuring out options.