Where I think Meteor is doing wrong with Blaze

Totally with you @mitar …what drew me to Meteor were the clear principles, the clear opinions, on which it is built. The result of standing by those principles has resulted in a phenomenal framework. Sure it’s tough standing by principles when things get tough, complex, or when sirens of change are singing sweet nothings in your ear… but nevertheless, please MDG, stick to your principles.

2 Likes

This was the theory.
You were supposed to be able to use Tracker outside Meteor, there was a repo and a website for it. But then it was never updated.

You didnt get my point, Sashko. As a monolithic Framework, Meteor made sense because all the parts got very well together. Like i said, template helpers being autoruns get very well with Tracker (by the way i think it’s quite an antipattern to use autorun outside template helpers). Now getMeteorData returning an object feels imho hacky. Also render() being not a computation (autorun) feels unnatural. So i think React feels less “meteorish” than Blaze.

The ability to switch the UI layer made Meteor less monolithic than before, but since you are still using a proprietary package system and the view layer is still coupled to the backend, you still have a monolithic (== not open) framework. Hence the lagging of “meteor-react” behind “true react”.

By introducing React, the framework feels (imho again) less cohesive, while still being tightly coupled. Worst of both worlds !

I hope it make sense…

4 Likes

I would absolutely love to read @evanyou thoughts on this topic. Evan is the lead developer of Vue.js which fits the same role as React, Blaze, or Angular and arguably shares some API design principles with Blaze. I recommend reading this recent introduction from him:

Apparently Evan is now working at MDG, so I feel like his Vue.js builder experience is highly relevant in the considerations for a Blaze future.

14 Likes

wow, Vue is amazing! its simplicity is stunning. That’s why I’m disappointed something so unnecessarily over-engineered as React has taken over.

3 Likes

Thanks for this and all the other level-headed insight @sashko

3 Likes

@faceyspacey Yea you’re right. Minimongo basically acts as an in memory cache. React ‘state’ is just local data that’s not persisted… the overlap would be Session in the global sense and template vars in the private sense.

React still uses ‘state’ to temp. save mode/database data. For example make an ajax request and then setState with the payload (however flux is typically used to share this data easier).

This brings us to the real problem we’re trying to solve: it’s hard to reach instance storage/state from other components in plain Blaze. Template instance storage isn’t easily reachable by other template instances like Session storage is.

Agreed. However, this is a good thing. I’ve burned myself hard by doing this. If you could reach in then it’s no better than using Session. Once things get unpredictable and changes start cascading it’s a disaster to debug. Like it or hate it, I have to admit the Flux/Redux flow of data is very predicable.

thereby enabling you to use its instance storage even when triggered on a child template!

This is clever. It’s also a double edge sword… if one uses this just as a workaround to reaching into a parent directly it’s not much better. IMHO it’s better and more predictable if you just keep the views very dumb so that they’re just collection data on event handler fire and then calling a function and passing that data. This keeps it easy to share and easy to test. For example:

// actions.js

// app can reactively change depending on the current song id
// put in namespace to mitigate globals
Actions.playSong = function(trackId) {
  Session.set('Music:currentTrack:id', trackId);
  app.debug('currentTrack', trackId);
}



// template(s).js

class MyComponent extends Meteor.Component {
    // call action and pass in ID
    ['click .track-container']() {
      Actions.playSong(this.trackId)
    },
}

class OtherComponent extends Meteor.Component {
    ['click .other-track-thing']() {
      Actions.playSong(this.trackId)
    },
}
2 Likes

I’ve been following @evanyou’s active (almost daily) work on Vue.js, which I think is awesome, and thought how weird it is that he’s working for MDG and has not ever promoted Vue.js to the Meteor community as a front-end option.

And it’s not like he just recently started working for MDG. A couple of months back he was working on this side branch of Meteor, testing integration of React with Blaze?

6 Likes

“There will likely be an official integration package after Vue 1.0 is out.”

1.0 came out just the other day.

http://forum.vuejs.org/topic/166/meteor-reactive-collection-support

1 Like

I agree on keeping the views “dumb.” I.e. stuff all your logic in model methods and just trigger them. As for Session storage and scoping it and whatnot, truth is you don’t even need “Session” storage. In global scope you can setup a few global reactive dicts for example:

Music = new ReactiveDict;
Music.set('someKey', true);

TV = new ReactiveDict;
TV.set('someKey', true);

Then in any helper or autorun utilize its reactivity:

class MyComponent extends Meteor.Component {
    // call action and pass in ID
   someHelper() {
       return Music.get('someKey');
   }
}

In short, you can namespace your “Session” variables in a shorter purer way than Session.get('Namespace:currentTrack:id'). You no longer need the uninformative symbol ‘Session’ nor do you need the pseudo name-spacing, which in your example had 2 tiers of it before :id.

Either way it’s not the biggest deal. I think when you see my component system in play, you’ll agree it’s a very natural fit and doesn’t have the problems you mentioned about a “reaching into a Parent.” The reason being that you aren’t “reaching”–you are already coding everything that has to do with “state” in a component designated as the “controller.” All the helpers, events (and methods) that deal with that state–if coded correctly–will exist on to the same class. Included child components will know nothing about it, even if their rendering depends on a parent controller component’s logical decisions concerning “state.”

Basically what I think is going on is this: Facebook blabbers on and on about how great they are because they have solved the problem of “state,” but this “state” problem is a problem Facebook created by taking the Virtual Dom Diffing route. A problem that barely exists for us in the first place because of minimongo. What that has done has left everyone in the industry thinking they gotta do something special with “state”! It’s likely why you felt compelled (and got “burned”) using instance storage–my guess is you reached up from a child template to get this instance state via Template.parentInstance(level) (an unofficial method many people are using thanks to the Template Extension library and others). But that makes no sense to us. It only makes sense when the entire component tree is unidirectionally being iterated upon, on any change as in the Virtual Dom-diffing scenario facebook has chosen. Blaze isn’t unidirectional. The interface isn’t at least. Picture our helpers/events as anywhere in space (like literally dots in space in the galaxy), each able to communicate to the rendered screen whenever they want, and wherever they want (well, to their template views and below). An event handler attached to one template can be triggered by a click on any child view, right. In addition, it can maintain its own state in instance storage. My system goes a step farther to insure helpers can as well, that way they are correlated to instance storage shared between these event handlers. What you get is what this is all about (and a boring concept at that): logical groupings of related code. Facebook hypes up the issue, making it about state, instead of pointing out the boring, but very important, concept this is all really about. In my version of Blaze, one parent controller component can have event and helper methods all dealing with the same state. That’s all React is really helping you achieve anyway–forcing you to stuff all the code related to a group of similar state into one component class, thereby decoupling child components and keeping them stupid and single-purposed. Duh!! But because the way React has everything trickling through each other at every instant unidirectionally, they very well could have [even more] dubiously created a system where each child component is no longer decoupled trying to do the work of a parent component. So they created this setState system and this convention of passing functions down the tree to solve that (the latter of which is particularly is retarded; not so much the assigning, but more so the passing from parent component to child component to child component until it eventually reaches its destination). My system–which is just a plain componentitized version of Blaze where the context for ‘this’ is controlled in an OOP way–implicitly handles that since both helpers and event handlers are applied to child templates while maintaining the same context.

To be clear, here’s what it looks like:

ParentComponent extends Meteor.Component {
   includes: ['ChildComponent'],

   someHelper() {
        return this.get('someInstanceProp'); //get() is the quick form of: this.instance().get()
   }
   ['click button']() {
       this.set('someInstanceProp', true);
   }
}

<template name="ParentComponent">
    <button type="button">Click Me!</button>
    {{> ChildComponent}}
</template>

<template name="ChildComponent">
    {{someHelper}}
  <button type="button">CLICK ME TOO!</button>
</template>

So as you can see the state is maintained by a “Parent Controller Component.” The child component is rather dumb and doesn’t need to worry about its state. It certainly doesn’t need a function passed down to it. It’s pretty Fing obvious. It’s pretty Fing obvious that this is how Blaze should work. Perhaps includes: isn’t even required, and all child components share parent helpers by default (they already do events because of how jQuery works!). You get the idea.

With the system I just described, you may be worried that you end up with components all sharing state, and overlap in methods. That’s not a concern because you don’t have to design all your components like the ParentComponent. Plenty can be more encapsulated like ChildComponent, i.e. just dumb included templates who get passed data. In essence, you only end up using instance storage in stateful components fulfilling the “controller role.” Being able to directly and declaratively say “THIS ONE COMPONENT CAN IMBUE CHILD COMPONENTS WITH KEY METHODS/HELPERS/EVENTS” ends up being very natural. Also, you pick which child components get included. You don’t end up cluttering the scope of all child components. Specifically, you use includes:.

Anyway, you get the idea. You don’t have to worry about unidirectional flow. You don’t need to worry about passing functions all the way down the tree. You don’t get called a moron novice coder because the first decision you make isn’t “where should my state exist.” It’s pretty damn obvious. And good code generally gets organized this way (in “logical groupings”) with most view systems, React included. It just takes a lot more intricate work with React. In short, in all these systems, the goal is to put state management code in one of the bigger components near the top of the tree! Usually you will have a layout component, then maybe one other layout-like component, then you get the main Controller style component. Then you get a bunch of single-purpose child components that shouldn’t deal with state. It’s not like most projects have you dealing with state in 3+ components (per page). Just think about the main React tutorial on their site, where they have a search field–all the data rows render automatically via props (though not as automatically and implicitly as in Blaze) and then they fumble around passing a function down the component tree to capture the result of what you enter in a search field. All that work, for what still can be just a declared event handler in the appropriate place. React is simply cleaning up their own mess, and then they wanna throw all these Facebook conferences and record all these videos of everyone cheering for how they have saved the free world of software development. Ha. They’re great at PR though! Perhaps that’s truly the only thing Meteor is missing, a better PR machine.

2 Likes

Yep, I think your example of using dicts is much better than namespacing session! I wish reactive-dict had an API that took an obejct and did a batch update rather than each separate key but that’s pretty minor.

I can’t wait to try out your version of Blaze, do you have an ETA?

I don’t want to come off as trolling or being abrasive but keeping track of transient state in a large app is still a problem in Meteor/Blaze (more so IMHO). I agree that keeping cached db data is solved for us in minimongo but Meteor does nothing for keeping ‘local state’ from getting messy.

I’m sure template variables may help but before those came along we just had Session (and using Iron-Router too), when I had an 80k-90k line meteor app with lots of local state (not db state) it was a real pain in the butt working with 3-4 people. It felt like playing whack-a-mole when debugging. Changing one part of the app could break another very easily.

Part of this is not organizing things properly and not having a lot of high quality engineers but overall I felt like Blaze leaves you with lots of things to trip over if you’re not careful. Otherwise you get the whack-a-mole issue. It also makes it incredibly easy to store state in the DOM with jQuery (like updating a data-foo value which causes brittle issues with lots of iteration.

Also having one flow of data and having UI self contained with all of it’s dependancies passed in as props helps fit the whole problem in your head easier… when you have data in 20 places and unpredictable reactivity you have to account for a lot of the app while debugging. This isn’t an issue when it’s just a couple thousand lines but I would have died to get that kind of predictability with the 80k line app.

FWIW, I do think Blaze can solve these problems without one way data flow or a single tree of state… something like your proposal is def. a step in the right direction.

2 Likes

We can learn from history. In history there are many many cases that backup B plan win. At present the attitude of kernel group is to refuse any B plan on Blaze. Even if community developed a new version of Blaze MDG will keep it in Meteor new release? None. So any development efforts of new Blaze was discouraged. No vision, no roadmap. Do not think developers have lots of idle time on a new platform. There are lots of choice in the world. If a new platform does not enlarge its own cutting-edge advantages instead to adapt other platforms it is risk to be covered by other platforms.

2 weeks. It’s a year worth of work on nothing but this. It’s a complete framework that wraps Meteor, not just components, not just models. Every single thing you will ever need to do you can do in class form, and you get lots of “automagic” like Rails. But less automagical and just what you would expect in 2015. As a long time Meteor developer, I got fed up a year ago and put my mind to do this. It basically answers the question: “if Meteor was Ruby, what would a Rails for it look like?” Lots of people have been asking MDG to do this, i.e. in all the requests for guides for how to build “large scale” applications. So my goal was to envision how MDG would do it if they had the time. What’s the perfect clean Meteor-esque interface they would come up with in a purely Class-based framework. Right now, I’m just wrapping up the documentation site.

The thing is we basically do have unidirectional data flow, and data pass in as props. Sure, helpers can request outside data from minimongo, but overall it’s one template including another, passing on data. Typically you have like a User model at the top, and then you have his relations: posts, and each post has comments. And they pass on their child data to child templates unidirectionally just as you would do in React, just as is the normal case in any app. Sometimes child components can request more data in Blaze/Meteor, which they can’t do in React. For example, the comment models could then request like counts/models, which are already present in mini mongo from another subscription. Envision that as runners running a marathon unidirectionally and more runners coming to join the party. That’s not too egregious, and likely a feature. Right.

The problem you speak of–i’m willing to bet without a doubt–is multiple conflicting autoruns, where the autoruns do “work” that includes setting reactive variables used by other autoruns. It’s not just the typical: event handler click triggers reactive var and another helper updates saying the Session.get('count') incremented from 2 to 3. It’s when you have autoruns (like helpers) which then trigger another reactive dependency to change, like a click handler would. That’s where the spagetti code happens in meteor. It’s where you can get yourself into a lot of trouble. It’s where we all have gotten ourselves into a lot of trouble. It’s the point in a project where we end up refactoring otherwise “good enough” code into excellent code, when otherwise we probably would have just left the code as is and kept it moving (time is money and a reality for us coders). And many novice developers are likely screwed once they reach this point.

What you end up doing is creating an interface for what’s going on that has one entry point. You throw out all the reactivity for a second, and just imagine the system as basic classes with methods. You say to yourself, what’s the main action that’s going on. It’s doThis(). So you move all the code related to doThis into that method, and anywhere else that uses it, you call it. I.e. a typical refactoring routine. One that might be complete already if this wasn’t a reactive world. Then–and I’m trying to remember an exact example–you go back to your 2+ conflicting autoruns with the intention of making sure the first autorun doesn’t trigger the second, which in turn triggers the first again. And here’s where it’s likely still broken and even the best you can do needs a better official MDG solution. You use if/else logic to make sure they dont trigger each other in a loop. Having moved all the offending code into single-purpose separate methods basically only served the purpose of making your autoruns cleaner to look at. You moved the offending code out of there so you could focus just on the if/else logic of how its triggered, i.e. get to the bottom of how they are cycling.

I love that you brought this up. I actually haven’t formally thought about it until now, but I do have a lot of tricks I use, and at the very least could produce a nice guide, but possibly even produce a class which inherently prevents this. The main thing is to say to yourself: “use as few autoruns as possible.” Often I see people have a lot of parallel autoruns with the goal of them isolating separate code that needs to autorun. That’s a good intent, now you have a wider surface area to get yourself into trouble–more autoruns all going at the same time. And not just helper style autoruns where the convention is to just return a value, but manually created autoruns where you might easily decide to add code to trigger some event.

Now that I think about it, the biggest thing causing this problem is likely relations! Without a relations system, you gotta use autoruns to request related data (if you haven’t invested the time to do it all in one server side publisher, which is challenging and most dont). Relations are why you have one autorun waiting for data from the first autorun/subscription, and then subscribing to more data based on what it receives, and so on. My system has what Rails’ relations and ActiveRecord would look like in the Meteor world, and has from the start. I haven’t manually created a subscription in almost a year. I get all relations and aggregates associated with the main plain subscription through static configuration:

class Post extends Meteor.Model {
  relations: {
      comments: {
            relation: 'has_many',
            model: 'Comment',
            foreign_key: 'post_id',
            //selector:
            //limit:
            //etc (and other good stuff)
      }
  }
}

let comments = Post.findOne().comments(); //voila

Now tell me that’s not how that should look in Meteor. Pretty obvious stuff. It’s where Meteor needed to have been gone.

Anyway, subscribing to relations in autoruns is probably the main cause of autorun-entanglement, “wackamole” as you call. I don’t think MDG grasps just how important relations are to our projects.

The next common offender is likely Iron Router. The way those routes re-ran and the work being done in them just got outa hand. My router is actually based on Iron Router, but you use it just as a dumb router like has become popular, thereby focusing on template-level subscriptions. So that’s one way to handle that. But even then you can end up with a pretty autorun or even set of autoruns attached to your top level controller component.

…So it hasn’t been as big of a problem for me as it used as I’ve unconsciously picked up the dos and donts with autoruns as I went, but now that you’re making me think about it, I have an idea what we need: autoruns in helpers basically work as they should. My hypothesis is it’s these manual autoruns that trigger actions that are the real problem, right. So that would mean we should develop more specialized autoruns. Autoruns that deal really well with subscriptions (better than just turning them off on computation stop). Autoruns that deal with session vars. Autoruns specialized in spawning other autoruns. Autoruns specialized in URL params, hashes, etc, changing. Autoruns almost always serve the purpose of setting some Session variable in a given circumstance or making some subscription. At least, those are the reactive things they do that could cause problems. They can do other stuff, maybe show a notification with jQuery, but that’s not will get tangled.

So how do we solve autorun cycles is the question. The only thing I can really think of now is a built-in debugger that will notify the developer when one is likely to happen. A visualization of static usage of session vars in autoruns. You could basically statically determine which are likely to collide based on which session vars are get and set. At least that would solve it if u only used Session (rather than reactive dicts which you may not be able to statically parse). And rather, it wouldn’t solve it, but give you insight. We could probably dynamically handle reactive Dicts and any other reactive data source by programmatically tracking dependencies at run time. And then be able to warn you that there is potential for a problem.

Another option could be flipping the equation around. So instead of focusing on autoruns, lets focus on what we are trying to do: set session variables, subscribe to the correct data. We could imagine a SessionSetter class of sorts, and we specify its dependencies. So before Session.set('animationComplete', true) is run, we configure a map of its dependencies:

class AnimationComplete extends Meteor.AutorunSession {
   depend: {
        route: 'docs', //autorun only applies to this route
        params: {slug: 'bla'}, //matching these URL params
        session: {animationStart: true}, //and this other session var equalling true
        func: function() { 
            return Subscriptions.documents.ready(); //and this function returning true
       },
       delay: 300 //then after 300ms, call the action() method below
   }
   action() {
        this.set('animationComplete', true);
   }
}

That’s just a quick idea. I can’t tell you whether it will stick. But the idea is just like MDG did with Meteor and Facebook with React: you take an “after thought problem” and make it priority and build a robust “ground up” solution for it. At the end of the day you can break the rules/conventions of React–it will just combust if you do. But just by having the conventions, you are “steered into the pit of success” (I think u used those terms in one of ur other posts lol). So at the end of the day, something like this is a lot less likely to get entangled. You are forced to explicit specify every condition required to trigger your autorun. Just by doing that you have braced and insulated your autorun, protecting it from potential collisions, i.e. from running when it shouldn’t be running.

Now imagine a Subscription-based one (something tells me this may be more exciting):

class LikeSubscription extends Meteor.AutorunSubscription {
   depend: {
       route: 'home',
       subscriptions: {
            Post: 'popular', //these both must be `ready`
            Comment: 'related'
       },
       func: function() {
            return Comment.likeCount() > 0; //why bother making the subscription for likes if there are none
       }
   }
   action() {
        Like.subscribe('all', {$in: Comments.find().fetchIds()});
   }
}

That’s somewhat of a contrived example–but my guess is there truly are better use cases than checking whether you already have a client like count above 0.

Anyway, nothing is stopping us from making this right now. It’s just a wrapper for tools we already have. Hell, I might build it into my framework. It’s not the most useful to me just yet, but I can see how it would be to newer meteor developers that don’t already have conventions and tools to minimize autoruns. I gotta think about it more. I have a feeling a “killer feature” might pop up here. For now, based on this, the idea is people need to just take their autoruns a lot more seriously, and conventions could help box that in. This is essentially the equivalent of “innocent before proven guity” while we have a “guilty before proven innocent” system currently. Currently, your autorun will run basically under any conditions; what you see above forces you to limit those conditions from the start and through named methods gives you various ideas of how you might wanna box it in (e.g. route, other subscriptions, session vars, params, etc). …Over and out for now.

7 Likes

on a side note, Cursor.observe/observeChanges() is already a specialized autorun. You configure it with what you wanna observe: added/removed/changed/etc. You aren’t just observing anything happening on that collection like we are anything that happens to variables used in an autorun.

So basically we take that principle and apply it to more things. And usually it’s controller-esque things (rather than model/collection things as in observe). You’re observing things like hash changes, when subscriptions are ready, Session variables containing “state,” e.g. what you fill out in a search field. Generally “entry point” type stuff, akin to parameters in a URL, but the “single page application” version of that stuff. That’s the idea hear–in the past you got everything you needed from the URL and cookies. Well, now all the actions you can take basically determine how the controller should behave at a fundamental level. You get the idea. We need a “Reactive Controller.” It just boils down to figuring out the most common constraints, occurrences, patterns, etc, and then embodying it i configurable code like observeChanges.

2 Likes

Like this?

2 Likes

It does!

http://docs.meteor.com/#/full/session_set

Session.set can also be called with an object of keys and values, which is equivalent to calling Session.set individually on each key/value pair.

Session.set({
  a: "foo",
  b: "bar"
});
5 Likes

Oh snap! Thanks for the heads up! :smile:

1 Like

I opened the following tickets:

After a discussion this could help move Blaze to Blaze 2 ideas, in a backwards compatible way. Any takers to implements PRs for those?

1 Like

The description of your system looks pretty the same as things in Blaze Components. I have not yet found a difference. Except for the fact that you link templates with components. In Blaze Components that was a conscious decision to break that link with a goal of reusability. So that you can keep the same component logic, but replace the template underneath (for example, with a different theme). Or that you can use the same template for multiple components. It is interesting to observe things you are writing because it looks very much like a progression of thoughts we made as well.

Especially with how important is to bound event handlers, template helpers and everything to the same, original, instance. What is also useful though is once you are able to call template helpers from other template helpers. So once they become template instance methods.

I have outlined some of ideas for this here: https://github.com/peerlibrary/meteor-blaze-components/issues/82

So by having a clear tree of components it is easy then to navigate it. Then you can use the pattern of all state is in the root component. Or you can do things like “find me a parent component of this type”. Or “parent component which implements this interface/methods”. And then you call their public APIs, which can potentially register reactive dependency, and voila, you have a great way to communicate between components.

3 Likes

Another pattern is to use a local only collection as a sophisticated session state data store, with the full power of minimongo.

5 Likes

Are/were there some official/actionable items put on the table?

9 Likes