I really appreciate Iron Router’s flexibility and how powerful it is, but lately one thing I’ve started having doubts about is having routes rapidly re-running reactively and randomly (now say that 5 times).
On the other hand, Flow Router uses a different approach:
You can’t have any kind of reactive code inside routes, but there is a reactive API to dynamically change your app based on the state of the router.
I haven’t really tried out Flow yet, but on a conceptual level that makes a lot of sense to me. Then again, maybe I’m just using reactivity wrong?
So my question is: is reactivity in the router a good idea?
I have not tried out Flow Router yet either but it is looking much more appealing the more time I spend with iron-router. Exactly like you said, the re-running of the routes to maintain reactivity seems like a sub-optimal solution. Before, my team was using iron-router mostly for the ability to manage subscriptions on the routes to maintain reactivity but with Template subscriptions this no longer seems as useful as it used to be. As our app grows we have been moving away from using iron-router to manage our subscriptions/data and are starting to use it purely for routing purposes.
To answer your question: I personally don’t think reactivity in the router is as useful now as it used to be mainly because of template subscriptions so we have stopped using it for reactivity and it seems to have decreased the complexity of our client-side code.
Sorry, I don’t know if that answers your question.
I think that Flow-router took advantage of the new features of Meteor for templates that allowed us to delegate things that previously did with iron-router.
Flow-router does what he has to do, the rest is up to the templates
I was actually just thinking about this. I don’t think a router should be handling data at all. I think it should do the job of a ROUTER and just ROUTE the user to the appropriate template and have the template logic handle possible re-renderings and subscriptions. I think it would make load times a bit better and make the code more maintainable.
I would actually love to hear the opinions of @arunoda and @cmather on this.
I think Router is a core piece in the app. Flow Router is different way to look at it. (May be the natural way as a client router)
This is how I think about it.
When someone visit a route, we will register and notify some stuffs. These could be rendering, subscription intiatiation, analytics and so on.
Then we can take actions based on that. Like rendering loading screens, re-route into another page. We took off that from the router with Flow Router.
That’s the idea and it works pretty well and gives us a lot control. But there is one issue. Using waitOn in the router is easy. But doing these checks in the layout or another layer is new to meteor developers. That’s where we are looking at some patterns and tools.
I quickly found Iron Router hooks, subscription management and data function error prone and very difficult to optimize, so I stopped using them. I am now waiting for useraccounts native support in Flow Router before I can switch (see here).
When we first started routing Meteor apps it seemed like a no brainer that it should be reactive and it certainly made things like user authentication and waiting on subscriptions fit nicely with the reactive system.
Over time it’s become increasingly clear (to me anyway) that the extra complexity that comes from the reactivity isn’t worth it and that the router should stick to just purely picking a top-level template to display. I guess that’s the idea that flow-router is running with.
However I do think it’s tricky to do some of the stuff that you’d typically do in a reactive iron-controller in a template due to Blaze’s relative lack of template lifecycle methods. Perhaps Blaze Components is the answer here.
That’s a really interesting topic and I haven’t really decided yet if there is “one best way” for routing. There are cases where reactive routes make everything more complicated and you really have to throw in a Tracker.nonreactive to deal with it.
BUT there are situations where reactive routes make stuff much easier, for example when you have a process like “purchase an item”. Then you can directly point the user to the checkout route which has a beforeAction with some conditionals and redirects to ensure that the user has billing information and credit card details entered step-by-step before actually rendering the checkout.
This could look something like this:
# Render the right screen depending on customer data
onBeforeAction: ->
customer = Prisma.Customers.findOne()
if not customer?
# No billing information yet -> let them enter it
@render Router.ROUTES.CUSTOMER_BILLING
@render 'customer_billing_information_header', to: 'header'
else if not customer.card?
# No credit card details saved yet
@render Router.ROUTES.CUSTOMER_CARD
@render 'customer_billing_information_header', to: 'header'
else
# Everything's ready for checkout
license = Prisma.Licenses.findOne isActive: true
# Only if the user actually has a trial license
# show the checkout for professional upgrade
if license? and license.type.isTrial
@next() # Customer + card + trial license = ready for upgrade!
else
@redirect Router.ROUTES.ACCOUNT
Which are only a few lines of code that save me a ton of overhead thinking about the screen-flow to checkout. Since this re-runs reactively I have nothing more todo than let it calculate which route the user should be on.
So my advice is this: Use reactive routing when it simplifies your problem at hand but be careful not to trigger side effects in these cases otherwise you will end up throwing your laptop out of the window.
I think the router should be dead simple – just an entry point to your app. I use FlowRouter to define a route, get the params and queryParams, and render the correct React component. I’ve offloaded all subscriptions and all reactivity into my components. This way, a component will work in any route, regardless of the subscriptions in the router that the component has no control over. This makes a lot of sense to me. The only problem here is Fast Render and SSR won’t work without your subscriptions in the Router. We could, however, inspect the subscriptions in the top level React component, but this won’t work a non-top-level component has a subscription as well.
So from my perspective, I just want a dead simple router. Just an entry point to the program. No reactivity. No subscriptions.
If all you need is a subscription based on what’s in your URL and you don’t do anything else in your template that depends on the URL, then I just stick to Iron Router.
However, if you’re using one template that does different things based on different routes, then you may want to use Flow Router because Router.current() is going to re-run no matter what changes in your URL. Flow Router will give you fine-grained reactivity on specific URL parameters.
I’ve also started to move all subscriptions to components for the same reasons @ccorcos mentioned.
As soon as I read about how FlowRouter avoids the unpredictable behavior of IronRouter I stopped everything else I was working on and spent the afternoon switching from IronRouter to FlowRouter.
Since then I’ve been able to more powerfully understand the internal logic of my app and I feel like I’m back in control again.
I love FlowRouter and my respect for Arunoda has gone up a big notch (and it was already high).
I’m in the same boat. Iron-Router is not bad, it’s a great tool, but for my own use-case I found it abstracted too much away such that I lost sight of what was actually going on. I’ll be switching my project over to Flow-Router this weekend.
Now you could say I’m just a bad developer (and that’d be true) but Flow-Router just seems like a time saver in its simplicity. It’ll take me more work elsewhere, but I think the app as a whole will be better structured because of it.