Where I think Meteor is doing wrong with Blaze

Sure, this is why I think having both Blaze and React is a good thing for us to have a choice :smiley: IMHO one is not clearly better than the other for every person/project.

I had trouble keeping my beer down when I read, a ‘game changer’. Not everyone can be so easily fooled by such obvious hyperbole.

This is just my opinion but a React component is more explicit about data than Blaze. That was the main point I was trying to convey. Being able to glance at something and know what it’s doing is important for me in large apps.

Also, when someone is always promising something better in a future version (0.14), it always raises a smell.

This is the current version now btw.

At any rate, to each their own :smile:

4 Likes

I’ve been working on a solution that once combined with @timbrandin’s https://github.com/timbrandin/sideburns or a forked version of it should eliminate the Blaze vs React debate. Below I’ll also introduce a solution for the Flux Pattern based on @SkinnyGeek1010’s amazing use of the “Alt” flux implementation ( http://alt.js.org ).

The general idea is a shared component-based interface between Blaze 2.0 and our own wrapper of React that looks almost identical to React minus a few things. That means component-based Blaze 2 interface (similar to @mitar’s Blaze Components) that is both a natural progression for Blaze and similar enough in interface to React to be agnostic of the underlying renderer. So you build your app with blaze, and at a later point in time you switch out the rendering engine from Blaze to React.

Here’s a quick example of the core things this shared interface must do to interface with both Blaze and React without requiring a transpiler of the non-template js:

class MyComponent extends Meteor.Component {
	//Instead of accessing props at `this.props`, we use it as a function so Tracker automatically makes it reactive.
	//And since the property won't actually exist at `this.props._id`. 
	//Under the hood the Blaze version of `this.props` is doing something like this: `return Template.currentData()._id`.
	//In other words, the props are the data context, which is passed from parent to child template similarly in both React and Blaze.
	
	//As for React, ots wrapped in such a way that `this.props(key)` simply calls a function that returns `this.props[key]`! 
	
	//And the same of course goes with `this.getState()`, which in the Blaze version would call:
	//`Template.instance().activeCategoryId.get()`.
	activeCategory: function() {
		return this.props('_id') === this.getState('activeCategoryId') && 'active';
	},
	
	//`this.setState()` in the Blaze version of course calls `Template.instance().activeCategoryId.set()`
	//My system creates one ReactiveDict in fact for each Component that requires it.
	'click ul.categories li': function() {
		this.setState('activeCategoryId', this.props('_id')); //note: setState can also take an object as in React proper
	}
}

There would be some caveats, the main one being that if you don’t follow all the unidirectional requirements of React, it would break with React as the rendering engine. That said there would be prescribed patterns that if you follow would prevent this. The major pro here is that we preserve the ease of development that Blaze offers to newer developers (and for smaller apps, or at the beginning of development of large apps). As @mitar has put it, the main concept here is that React is akin to a “strongly typed” language whereas Blaze would be a “weakly typed” language. That means React notifies you right away when you’re doing things wrong whereas Blaze lets you get away with it. Things like optional linters could be implemented to curb issues here as well, as well as checks in our abstraction itself. But inevitably not all could be caught. And that is actually fine–it lets you “prototype” your app (like Meteor always has excelled at), and when you want to switch to React (say because you want to start moving parts of your app to React Native, or take advantage of other things in the React eco system), you deal with all the “typing” errors or whatever you wanna call them then.

In addition, in this shared abstraction we simplify and automate a lot of things that are verbose with React. My personal opinion is that React and Flux are great architecture patterns, but horrible interfaces. This is clearly shown by all the Flux implementations (minus the fact that Facebook originally implemented Flux generically to display the architecture design). And this is to be expected, it’s a natural progression–the core ideas have been presented, and now it’s time to do it better. I wouldn’t expect the first implementation to be the best (think of Google vs Yahoo and the other original search engines).

So, we can do this for the React architecture in a way that actually uses React as the renderer but does so in a Meteor/Blaze-consistent/appealing way. If we do this right, the real idea here is that we only have to transpile Spacebars (taking into account helpers and events defined on components and automatically attaching them as props), and the javascript interface in the components is the same for both. The system will know which renderer you’re using and switch out the code called for things like this.props() and this.get/setState();

The most important things to consider though isn’t transpiling this shared interface, but where the data is coming from. This is especially important with Meteor since this is where React leaves off and Meteor definitively takes over. That “takeover” must be on point. There is a lot to be learned from both the Flux pattern and Relay. The reason being that, when either is used correctly, the end result is a unidirectional chain of “dumb/pure” components, which is the crux of making an app that can render both with Blaze and React. Every component being a “smart” component is exactly where all our Meteor/Blaze apps go wrong. The second data is queried into a component that the “react component” wasn’t reactively aware of via getMeteorData everything breaks down. Relay and Flux style “state” management offers a lot more capability than one autorun running in getMeteorData. They however are both unnecessarily verbose (I mean you’re now requiring lots more code and complexity of your developer), and are redundant with a lot of what Tracker and Minimongo already do.

So the first step is deciphering the difference between “data”-based state from Minimongo, and what I’m calling “template only” or “template specific” state. We need solutions for both, the latter of which is one of the core problems Meteor developers are having when they move from global Session storage to “scoped” storage on instances.

Here’s an example of the interface I’m working on that addresses Relay style template-level minimongo “data state” that trickles down to child templates/components:

class MyComponent extends Meteor.Component {
	subscriptions() { //ran in `getMeteorData` (Mongo-specific Relay style template level subscriptions)
		return [
			//this is my automamagic subscription system (/w relations + aggregates):
			{model: 'Post', name: 'recent', limit: 10}, //Post.recent() called below will return only the contents of this subscription
			{model: 'Post', name: 'one', with: 'comments', params: '_id'}, //:_id param in URL
			{model: 'Category', name: 'all'},
			function() {
				//if you use a function to subscribe to data, you can reactively depend on
				//data from other subscriptions as the data becomes available:
				if(Post.one()) Author.subscribe('one', {_id: Post.one()._id}); //though the relations system above would also handle this [contrived example]
			} 
		];
	},
	
	//minimongo "data state" which can be accessed via `this.data()`:
	categories: function() {
		//note: in Meteor.Model I have features to handle subscription scoping via generated methods
		//based on the subscriptions listed above. the subsription 'all' in fact is a complete description of 
		//what's passed to `find()`, which exists on its Model on the server. You have options to parameterize it
		//from the client to an extent, but the server has complete control what's rendered.
		return this.data('Category').all(); //accessing "sideways loaded data" guaranteed to only be
	},
	posts: function() {
		return this.data('Post').recent(); 
		//So in both these helper methods, we are accessing "sideways loaded data" guaranteed to only be
		//the documents you want, and guaranted to be only ones that re-render the template because we use
		//generated methods that precisely represent the document sin your subscriptions.
		//`all` and `recent` are the generated methods based on your subscription configurations.
	},
	
	
	//It's important we distinguish with the above "sidewise loaded" "data state" from mini mongo
	//which is very much automatically handled for you vs "template only" style state below
	//which requires handling unidirectionally ourselves in order for the React renderer to behave:  
	
	
	//"template only" style state:
	onCreated: function() { //with the React renderer would call `componentDidMount` -- both could be used interchangeably
		let categoryId = this.data('Category').all().fetch()[0]._id;
		this.setState('activeCategoryId', categoryId);
	},
	
	//alternatively, getInitialState will do the same as `onCreated`
	getInitialState: function() { 
		return {
			activeCategoryId: this.data('Category').all().fetch()[0]._id,
		}
	},
	
	//helper that uses "template only" state:
	activeCategory: function() {
		return this.props('_id') === this.getState('activeCategoryId') && 'active';
	},
	//event handler that uses the same "template only" state:
	'click ul.categories li': function() {
		this.setState('activeCategoryId', this.props('_id'));
	}
}

Now let’s look at the actual templates to see how this all comes together:

<template name="PostComponent">
	<div>
		<h1>{{post.title}}</h1>
		<p>{{post.content}}</p>
	</div>
<template>

<template name="MyComponent">
	<div class="blog-list">
		{{#each post in posts}}
			{{> PostComponent post=post category=activeCategory}}
			/**
			Will transpile to `<PostComment post={this._get('post')} category={this._get('activeCategory')} />`
			`this._get()` will first search `this.props` and then `this.state`.
			
			Actually that's not how this would transpile since we'd have to first transpile #each into a pure js loop,
			but it shows as an example how `props` and `state` are handled internally by `this._get`. The idea being
			that React has you distiguish between State and Props, but in spacebars you don't. There obviously would be
			a performance reduction as a result, but it's likely small. Smaller than how Blaze looks up tokens by far.
			
			You could be explicit for performance (at the expense of verbosity and looking less like Blaze):
			`{{> PostComponent post=props.post category=state.activeCategory}}`.
			
			The main point is that your code looks very much like the good 'ol Meteor/Blaze you're used to
			except you're forced to explicitly pass the context in a way symetrical with how `props` are passed in React.
			This is similar to how `this.props()` works in helpers/events/etc: in React it simply returns this.props.thing
			but in Blaze, because of Tracker, you need a function to be called. So we use a function in both interfaces,
			but in React simply call the standard way via one extra short frame in the call stack. 
			**/
		{{/each}}
	</div>
		
	
	//Lets look more deeply at how we would iterate through an array/cursor
	//and also let's see how in fact we don't need to explicitly name passed props through "spread attributes":
	<ul class="categories">
		{{#each categories}}
			{{> CategoryComponent}}
		{{/each}}
	</ul>
	
	/** would transpile into:
	let cats = this._get('categories').map(function (cat) { //_get finds categories on props, state OR METHODS
	  return (
			<CategoryComponent {...cat} /> //spread attributes!
		);
	});

	return (
		<ul>
		  {cats}
		</ul>
	);
	**/
</template>

Now let’s look at how helpers + event handlers are automatically passed down:

<template name="CategoryComponent">
	<li class="{{activeCategory}}">{{name}}</li> //there is a click handler attached to .category!
<template>

The transpiler automatically passes down any click handlers to elements
with matching selectors in child templates (and their children’s children and so on). And automatically
passes any helpers as props through intermediary templates until it reaches any child templates that implement them.
The state for both the helper and event handler of course is maintained by the controller component at the top:

<li className={activeCategory} onClick={this['click .category']}>{{name}}</li>
//note: probably can only work statically at compile time.

So what’s most important is how this child component’s “template-only” state is managed by a Parent component who houses the helpers and event handlers. The event handlers and helpers are automatically made available to child components. I see that as our biggest challenge in making React compatible with the shared interface we’re going for. It’s also the crux of how “template only” state should be managed in Blaze in general regardless of React. In plain Blaze event handlers are already handled this way; just the context of the top level parent instance is lost. My components system as well as @mitar’s Blaze Components solves that. And now we also pass down Helpers, and of course maintain the context there too. So that means in Blaze we are saved all the work of passing down all these props and handlers. In react, the transpiler does the same thing for us. In essence, we bring a lot of the automagic Blaze offers surrounding contexts to React. And in Blaze we finally have a system/pattern to put plain “template-only” state in the right place at the top, in a way that’s easily and naturally reachable by child templates.

This doesn’t solve leaf components that need to share state. And any significant size app will soon have that problem.
It’s what lots of people have been discussing in this thread and in others–besides clobbering global Session storage
how can we manage state in a scoped unidirectional way that multiple leaf components can accesss???

Here’s my solution–or rather proposed solution. Whereas the stuff above is actually all built and being used by me,
these are ideas I just started working on thanks to @SkinnyGeek1010’s https://github.com/AdamBrodzinski/meteor-flux-leaderboard
and Alt which his work there introduced me to: http://alt.js.org . Alt is one of the cleanest Flux
implementations I’ve seen, and really nails some things. I still thinks the interface is far from what I’d like it to be,
but that’s where the following comes into the picture:

class MyActions extends Meteor.Action {
	//configuration method to define basic actions.
	//They only dispatch action (of some name) with any arguments:
	simpleActions() {
		return ['somethingHappened', 'triggerEvent', 'tweets']; 
	},
	
	//do more than just pass arg to listening stores:
	foo(param) {
		//do some work with param
		this.dispatch(param);
	}
	
	//showcasing async:
	findTweets(searchTerms) {
		this.dispatch(); //dispatches 'findTweets' since no action name is passed
		
		Meteor.call('bar', searchTerms, (error, result) => {
			this.dispatch('tweets', result.tweets); //showcasing calling another action
		});
	}

	//trigger multiple actions from one action:
	triggerSeveral(param) {
		this.dispatch('somethingHappened');
		this.dispatch('triggerEvent');
		this.dispatch('foo', param);
	}
}

class AnotherActions extends Meteor.Action {
	//just another Actions to serve as an example of the many-to-many relationship between Actions and Stores
}
		
class TwitterStore extends Meteor.Store {
	actions() {
		return {
			MyActions: ['findTweets', 'tweets'],
			AnotherActions //es6 shorthand for: `AnotherActions: AnotherActions`, which causes store to bind all its actions
		};
	}
	
	onFindTweets() {
		//setup empty tweets array some view components see its empty and show a loading spinner
		this.setState('tweets', []);
	}
	onTweets(tweets) {
		//perhaps do some manipulation of the tweets
		this.setState('tweets', tweets);
	}
}

class AnotherStore extends Meteor.Store {
	//just another store to serve as an example of the many-to-many relationship between Components and Stores
}

class TweetComponent extends Meteor.Component {
	subscriptions() { 
		return [
			{model: 'Post', name: 'recent', limit: 10}, 
			TwitterStore, //unlike actions <-> stores that may bind together only some actions, the idea behind subscribing
			AnotherStore //to a store is you get all of its state & its up to the component to use from state
		];
	},
	
	'blur input': function() {
		//calls this.ref.search in React, and $('[ref=search]') in Blaze; NO NEED TO TRANSPILE JS LIKE TEMPLATES!
		//We wrap React with facade layer similar to how we do with Blaze Components
		let searchTerms = this.ref('search').val();
		MyActions.findTweets(searchTerms);
	},
	
	//also note that subscribed to stores automatically apply their state to initial state
	tweets() {
		let tweets = this.getState('MyStore').tweets(); //similar to how `data` is scoped as a param to this.data('Model')
		return _.isEmpty(tweets) ? null : tweets; //in Blaze will only re-render if `tweets` prop of MyStore has changed
	}
}

<template name="TweetComponent">
	<input type="text" ref="search" />

	<div class="tweets">
		{{#if tweets}}
			{{#each tweets}}
				{{> TweetComponent}}
			{{/each}}
		{{else}}
			{{> Spinner}}
		{{/if}}
	</div>
</template>

So first, a few things must be discussed so we are on the same page with the goals here. As usual, everything
coming out of the Facebook camp is unnecessarily complicated. Flux and its benefits can be explained a lot more easily. The core concepts of Flux are 2 sets of many-to-many
relationships:

  1. Events <-> Stores
  2. Stores <-> Components.

An event-based system is prescribed to make those relationships. All in all, its purpose is to decouple these 3 aspects. It’s not
much different than a many-to-many or through relationship, say, in ActiveRecord, or any event system that allows you to move endlessly amounting logic out of the core event emitting object into however many client objects want to listen to it. It should be configurable
via the least amount of syntax possible. Redux attempts that through a functional reducer-based system,
but it doesn’t fit with the configurable & consistent class-based system I prescribe.
A few classes have been presented here–there commonalities should be apparent. The subscriptions property
initially displayed is pure configuration, and that’s just the tip of the ice berg for how my system works.
In short, the Models class (not discussed here) is chalk-full of configuration, same as many other utility classes.

So with the goal of reducing our code as closely to pure configuration as possible, what you’re looking at
is simply specifying those relationships between actions and stores and then with stores and components, much
like Alt. Alt is the inspiration. And it was actually the perfect inspiration for the style of my overall system.

Now back to the above example. It’s kind of a contrived example because you only have one leaf component
in need of state. Its purpose was just to first show how it would work. Lets move to adding a second leaf component
that needs to share the state:

<template name="PageComponent">
	{{> HeadlineComponent}} //leaf component A
	{{> TweetComponent}}  //leaf component B
</template>
	
<template name="HeadlineComponent">
	<h1>CURRENTLY SEARCHING: {{searchTerms}}</h1>
</template>
	
	
	
class HeadlineComponent extends Meteor.Component {
	subscriptions() { 
		return [
			TwitterStore
		];
	},

	searchTerms() {
		return this.getState('MyStore').searchTerms();
	}
}



class MyActions extends Meteor.Action {
	//...
	findTweets(searchTerms) {
		this.dispatch('findTweets', searchTerms); //now `searchTerms` are passed to any stores that want to put in state
	
		Meteor.call('bar', searchTerms, (error, result) => {
			this.dispatch('tweets', result.tweets);
		});
	}
}
class TwitterStore extends Meteor.Store {
	//...
	onFindTweets(searchTerms) {
		this.setState('searchTerms', searchTerms); //and now `searchTerms` is state shared by both leaf components
		this.setState('tweets', []);
	}
	//...
}

They can now talk to each other unidirectionally without having to query state from each other. The TweetComponent can display the tweets and let you search for tweets, and the HeadlineComponent can display the search terms. Neither maintains that state.

The job of asyncronously searching for the tweets (and keep in mind that these are fresh tweets directly from twitter) is in an Action. I actually copied almost exactly what is shown in the Alt tutorial in the async section: http://alt.js.org/guide/async and rewrote it in this style.

If you read that page, you will see that the decision is yours about where you put the async work. He doesn’t higlight what factors might help you make that decision, but to me it seems obvious: if you have multiple stores that depend on the results of the async call, put the call in an action, but if the action is specific to one store, put it in the store. I originally had my example doing just that–it retrieved facebookPosts as well, and dispatched those to the Facebook store. But then I realized I was making the example too complicated. Anyway, you get the idea–the async call should go in TwitterStore.

All in all this highlights the flexibility of this decoupling–Actions can talk to multiple Stores. And conversely, multiple Actions can speak to the same Store. Similarly, Components can bind themselves to multiple Stores, and a Store can be bound to multiple components.

In the system laid out above, stores are treated just like Subscriptions–you’re subscribing to a store of “template-only” style data, rather than mini mongo data. Its data however is put in regular state rather than the .data property like data from getMeteorData, i.e. the subscriptions called within it. In both cases, you don’t have too manually fetch the data or set the state–that’s automatically handled for you by specifiying the “subscription”. And in both cases you have a symmetric API to get the scoped data when the subscriptions resolve: this.data('ModelName').subscriptionName() and this.getState('StoreName').stateKeyName(). There are a few other options there for how that could look, but since in the rest of my system you are doing things like Post.all() which calls Posts.find(/* subscription config selector + options */), decided that this was the most consistent. Another option would be this.data('Post', 'all'). But as you can see that is less reminiscent of the way that involves the subscription-named methods I tack on the Model class. However, it does have one benefit: it looks more like something will prevent you from fetching data not scoped to you’re subscription–it feels almost as though that’s the only way to get the data. The idea being we’re trying to steer the user into the “pit of success” of not fetching data subscribed to from the current subscription, which would break React. Anyway, from where I am now, I favor preserving the Model.subName() interface (or something closer to it) over babying developers. That in fact is my main gripe with React. I rather have the option to make all the mess we Blaze developers have made these past few years, but with new prescribed patterns to avoid it, not to mention these new tools which guide you in that direction.

Anyway, in conclusion, via the subscriptions config array you have a Mongo-specific Relay style interface, and via Meteor.Actions and Meteor.Store you have a Flux interface. As for the Relay style interface, Meteor automatically prevents double-subscriptions to the same data-sets, so it’s like Relay in that way–you can have multiple leaf components subscribing to the same data without worrying about double-subscribing. You can then use that data to trickle down the tree of components below each leaf. If however you have “template-only” state (which is needed by more than the children in one tree, i.e. leaf components), you can use the Flux pattern. And if however, you have “template-only” state which is needed only by the children of one “Controller Component”, then you can use the unidirectional top-bound event/helper instance state technique I first showed. That in fact is what I’ve been using for a while now, and quite successfully. I’ve been falling back to Session storage for leaf component communication, and have been wondering how to solve it, and above is the solution I’m about to build. Any input would be much appreciated because I’ll be opening my framework up to all you guys soon.

Lastly, I wonder if any minimongo state would ever make sense in the Flux style Actions and Store components–is relay style “sideways data loading” via subscriptions in controller components enough? I’ll leave that for another day, but I imagine it too could easily find a home in the aforementioned Flux style classes. I’m excited to see how much value they add, rather than clutter it only seems to produce in the plain React world. Not to dog React too much, but Minimongo goes a long way for us. A big part of my research here has been deciphering the difference (the strengths and weaknesses) of Minimongo, and other kinds of state. All the state discussed in the React world isn’t assuming you have all the benefits of Minimongo and Tracker at your disposal. You essentially have a lot less “state” to deal with because of it. So rather than jumping to creating all your Flux stores and actions before you make your first component, I see a future where only once you add that second leaf node do you begin to abstract your state code out into an Action and Store. That’s a major win for the Meteor community because one of the biggest selling points of Meteor is a novice can easily dive in. We don’t want to lose that. Someone just coming from HTML/CSS can bootup Meteor, bundle up some static web design code, and quickly start moving to becoming a dynamic developer. I see React (and especially Flux) being 25% or less as effective at helping that novice web designer level up their skills into becoming a full on web developer. This is also important to us more skilled developers because we work with web designers, because we do smaller projects, because we like to prototype quickly, because there’s so much to think about already–any productivity gains are a big deal for us. I hate the word “automagic”, but add some of it to these newly emerged paradigms/patterns, and we can get all that stuff with a lot less code, a lot less files. We can achieve the dream of both having a toolset that is easy to reason about as your app gets larger and swift and natural to get up and running with minus the boilerplate.

Super final thought: after discussing all the Flux stuff we can get get, I forgot to tie it back to the shared Blaze/React interface. The idea there is that the Flux stuff is actually super easy to build just for Meteor and my class-based system, and we will have that. But how does Flux transpile (or whatever)? Basically, it’s the same as my prescription for the React/Blaze merger: use functions instead of props. That way blaze can reactively re-render. The Flux implementation too will have a more pure event-based solution under the hood that doesn’t rely on tracker that propagates props/state from actions to stores to components. Hell, it could maybe even use Alt under the hood. I haven’t thought that far yet. First things first, if we can get React to render for Blaze 2.0 (even if a few constraints are required), we’re going to have a bright future–one where we don’t have to watch in disappointment as non-Meteor developers use a bunch of effective tools we can’t. At certain points early on NPM was something that we struggled with. Arunoda’s package to use NPM outside of packages only came out like a year and a half ago. Async on the server is still an issue on the server for those that haven’t groked how to use futures. Either way we’ve gotten over this stage and most of have no problems including NPM packages. Just look at how many are wrapped in Atmosphere. So a path like this will get us the same for the React world. I hope to release my Components system soon so it will shed even more light on how easy it will be to transpile to React. @timbrandin’s Sideburns is a step in the right direction, but it’s for Blaze 1.0. That’s a mismatch. That doesn’t lead to an environment where you can just switch out the renderer and have your Blaze Components look almost identical to what React is actually rendering. And that will be crucial for debugging, taking advantage of other React tools, and switching back and forth between Blaze and React. It will likely be crucial for actually meeting all the goals of being able to 100% transpile to React in the first place–Sideburns still has a ways to go. In conclusion, Blaze Components 2.0–whatever you wanna call it–is more important than ever, no matter which path we end up taking–but hopefully we end up taking the middle path.

11 Likes

There are developers/organisations who invest a lot of time and code into their own product.

There is a danger this approach is for stable maintainence of Blaze-as-is with only fixes and non-breaking changes.

There is a lack of clarity whether these investors-in-code was that kind of stability or favour development of Blaze over any other technology.

After my first explorations into React (and contemplating what transition to it might look like for an existing app), one concern that stuck out to me is that my app has dependencies that use Blaze.

Fortunately, React and Blaze play nicely together. From the Meteor-React tutorial, we can see how it simple it is to render the accounts-ui login buttons in a React class.

AccountsUIWrapper = React.createClass({
  componentDidMount() {
    // Use Meteor Blaze to render login buttons
    this.view = Blaze.render(Template.loginButtons,
      React.findDOMNode(this.refs.container));
  },
  componentWillUnmount() {
    // Clean up Blaze view
    Blaze.remove(this.view);
  },
  render() {
    // Just render a placeholder container that will be filled in
    return <span ref="container" />;
  }
});

But there are still architectural implications of using both. Why use two tools to accomplish one task? Many community packages depend on Blaze.

My app has substantial dependencies that use Blaze, like aldeed:autoform. In the short term, even if I wanted to transition to React, I’m still stuck with Blaze (and so often mentioned here, the state of Blaze appears unclearer with each passing day). I could find replacements for all of my dependencies that use Blaze, which would be a significant application overhaul.

Wrapping Blaze dependencies in React classes would work, but in my opinion, adds unnecessary complexity with no real added value. I’d prefer to only have one templating engine, 100% of the time. So, for now, adding React to the Meteor stack isn’t replacing Blaze at all. It’s simply a new dependency.

6 Likes

I know what you mean… I’m using Tabular tables and don’t have time to re-write it in React, though it’s so hard to customize I might have to re-write to get different functionality.

This can be a big issue for existing apps, especially if you’re using tightly coupled packaged. I hope more package maintainers can take inspiration from FlowRouter.

I’m also hoping someone will build an auto-forms that is decoupled from the UI so that we can use it with Angular, Blaze, and React without much more than a thin adapter.

My app has substantial dependencies that use Blaze, like aldeed:autoform.

If React was worth migrating for your app and you wanted to eventually migrate off Blaze you may be able to use one of the React Forms components that have similar functionality and use your current SimpleSchema with that.

However these won’t have the auto insert/update functionality but that could be added in a couple of functions (personally I wouldn’t want my UI to know about my database but that’s just my pref.)

https://react.parts/web?search=forms

4 Likes

I think react would be a fine way to go. I have used both and the “it’s easy to use” trade-off doesn’t really make much sense to me. React may have slightly more code, but in the end the complexity is more controlled IMO, which makes it easier to debug and what not.

Also, the JS community evolves way to quickly for meteor to not keep up. There is no reason for mdg to build blaze 2 just to try to one-up react/angular2 just to have it fall behind in major ways in a few weeks. Meteor has always (for me) been much more about real-time data and easy-to-use back-end plugins than blaze. As long as react has easy ways to tie into that reactive data, I am totally fine with it. Seems like a total waste for them to spend all that time on Blaze 2 when it is just going to fall behind when they move to improve DDP and other things.

Meteor has always been about creating a full-stack of tools that cohesively work together. But that doesn’t mean all the pieces have to be made by them. This seems insane to me. Just pull in react as the view (or webcomponents or whatever).

7 Likes

@jeffshaver, allow me to present a different argument for your consideration:

“it’s easy to use” trade-off doesn’t really make much sense to me.

One should absolutely never underestimate the power of ease-of-use, which is largely the result of cohesiveness; Touch-screens existed before the iPhone. Software existed too. Heck! smartphones existed before the iPhone and yet, it was the cohesive and easy-to-use combination of those things that created the product that basically ended the “PC Era”. i.e. browsing the web on iPhone was just… browsing the web! The rest is history.

React may have slightly more code, but in the end the complexity is more controlled IMO, which makes it easier to debug and what not.

Look around this forum. People often struggle with broken package integration, incorrectly learned patterns and deployment issues. Folks can’t simply absorb all the various documentation and details about all the software that’s out there. That’s what’s causing the bugs! It’s rushed training spread thin across too many tools.

the JS community evolves way to quickly for meteor to not keep up.

They don’t have to “keep up”, as long as they offer something cohesive that people can use to get the job done beautifully, deploy to Galaxy and enjoy a beer, they’ll keep having clients.

Meteor has always been about creating a full-stack of tools that cohesively work together. But that doesn’t mean all the pieces have to be made by them

I beg to differ. It may sound insane, but I actually think they should make even more pieces and add to core. Geoff Schmidt (CEO of Meteor) mentioned that some companies only manage to do 3 out of 100 applications that they need (this figure might have been his anecdotal hyperbole, but you get the point :sweat_smile: ). Why that is? Most current frameworks aren’t well integrated nor easy to use. Meteor has been trying to address those issues with a full platform + Galaxy. Start dropping pieces of the ecosystem and we’re back out in the Javascript wild west! (not that there isn’t any gold out there)

Just pull in react as the view (or webcomponents or whatever).

The words of Mike Lazaridis, co-founder of RIM (blackberry):

"if this [iPhone] catches on, we are competing with a [handheld] Mac, not a Nokia.”
And what is a Mac if not its cohesive ecosystem?

Thanks for reading!

9 Likes

This point struck me… I’m currently using a Mac as my main computer, and an iPhone. On my iPhone I mainly use:

  • Facebook messenger
  • Slack
  • Gmail
  • Reddit
  • Google
  • Camera app
  • Safari
  • Google Maps
  • Lyft
  • Yelp

You get the point - only two of those apps are actually made by Apple. In fact, I despise most of the native apps like iCloud, Apple Maps, etc. In fact, the whole point of the iPhone is that it has the best app ecosystem, not that it has a bunch of good stuff built-in.

I could tell the same story about the Mac:

  • Chrome
  • Atom
  • iTerm 2
  • Slack
  • Spotify

So I would say the point about Apple actually demonstrates the opposite - that having a platform that supports a vibrant ecosystem of different options, while having a stable base layer (in Apple’s case, the hardware and OS) is actually the winning play.

6 Likes

Great! That’s exactly what I was going to say, let’s use Blaze sintax and write renderers in whatever you want.
Are you going to further develop your blaze-react integration?

@sashko, first, thanks for reading my post!

the whole point of the iPhone is that it has the best app ecosystem, not that it has a bunch of good stuff built-in.

These apps you mentioned really are “downloadable built-ins”. If you look closely, you’ll notice they have too much Apple DNA in them. Apple sets the trend for design, the libraries, the integration, the IDE, the language. They guard the gate to the app store. They let other people build their built-ins for them.

Open up the app store on your phone. Choose something third party from the trending categories. Before you open this app, how do you expect it’s going to look, feel and work? How is it going to talk to other apps? What’s the integration like? If it looks, behaves and talks just like an Apple app, then what’s the difference, to the eyes of the consumer?

having a platform that supports a vibrant ecosystem of different options, while having a stable base layer (in Apple’s case, the hardware and OS) is actually the winning play.

Windows PC’s and Macs both have had a good base layer with a more than vibrant ecosystem, but they’re both starting to mimic more and more, the curated mobile ecosystem and its added simplicity and integration. That’s the difference. I just wanted to make that point clear. The winning play goes a bit beyond the stable base layer.

I think the Guide is definitely a great step in the right direction. I’m curious to have another look at React and one thing that stops me is the fact that I depend on their documentation, which is not integrated with Meteor’s. So I have the feeling that I’m using wrong patterns that will break at any moment. If the guide provides some ideas and examples to integrate with React (and Angular and Blaze), that’d be awesome.

except for developers are the users in Meteor’s case, and that completely changes the dynamic. Besides, no matter who makes the app on the iPhone, it’s still seamless for the end user. It needs to be that with us.

Then you’re one of the rare few, for whom Meteor wasn’t all about the client.

What people are not seeing is that React forces you to essentially say good bye to tracker at the top of all your client code, in your controller component in getMeteorData. But the entire client side experience was built on Tracker. So effectively you clean cut switch from one paradigm to another. A paradigm where this.props.bla is magically populated for you. You use to call functions, and you understood that tracker did something behind the scenes to know to recall it. Both are interesting paradigms, ones that have a minor learning curve coming from the pre-reactive javascript era of jQuery, which many are still coming from. So now what you’ve done is have introduced not just one, but 2 paradigms, you must learn.

Tracker worked seamless from Collections to Templates. It was continuous thread of though, a consistent way of doing things. TRACKER WAS METEOR. Not whatever backend stuff that was going on where you had no reactivity. Client side reactivity based on Tracker was meteor, and it still will be for the other 50-75% of stuff you’re doing on the client while using React. It’s not a seamless user experience. Most of us in this forum could handle it, but starting out, and being more of a newb programmer, the answer is NO. It’s not the seamlessnes that Meteor once was. @sashko Meteor as a whole is no longer the frictionless Apple Ecosystem.

3 Likes

Are there going to be any official actionable items coming out of this thread?
Is the discussion going to move to a better forum?
Are any of the decision makers going to participate?

Could someone @mention me if any of these things ever actually happen? I’m getting tired of coming back to this thread only to see no progress on anything that matters.

3 Likes

@funkyeah I will let you know mate.

2 Likes

haha, thanks and cheers mate

yea, people are working on it. Make a Blaze Components interface that can also transpile to react. We don’t need to wait around for MDG for anything. Blaze Components by @mitar basically is the interface, plus a few tweaks. And @timbrandin is already working on tools to transpile Blaze as it is. I outlined the plan in depth above. But here it is again:

We’ll have our own extremely thin wrapper over React that modifies React ever so slightly so you call: this.props('key') instead of this.props.key (Blaze Components will share the same interface)–that way the same code in Blaze can call a reactive function under the hood; and in React, the former simply calls the latter under the hood. And obviously, that’s just one example of the things that must be done, but it exemplifies the idea.

That’s my proposal at least, and I’m currently working on it (along with a matching Flux clone [based on Alt] that can similarly work with either underlying renderer). Any ideas are welcome; I’ll share my work when when I have some.

I appreciate the work you are clearly putting in, and I hope you find some way to make it financially viable for you to maintain your solution so people can depend on it long term. So don’t get me wrong but the first word in your response should not have been yes.

This thread was started by @mitar and in my opinion his original post was not primarily about finding practical solutions for keeping Blaze moving forward (clearly he wrote Blaze components without MDG). It was primarily about the erosion of trust by effectively dropping development of a core piece of their tech stack. That trust has now been eroded even further because the community has raised the concern but hasn’t been able to get any official response on it for over 16 days. At this point I think whitehouse.gov needs less attention on a topic to get an official response from the fricken president than we need to get a official response from MDG.

10 Likes

I wholeheartedly agree with mitar. I myself was attracted to Meteor because it claimed to be a platform where all the pieces to build things were there, rather than being another Frankenstein project assembled from multiple open source projects. I’ll be the first to leave if MDG loses its vision.

2 Likes

@arnpham as somebody building and maintaining a Blaze production app I share your frustration. However, I am hopeful about Meteor embracing more external technologies (react, npm, webpack, etc.)

The JS world is changing at a dizzying pace, and as an individual it feels impossible to keep up with all the new technologies as they roll out in rapid succession. MDG is a well-funded, insanely smart group whose full-time job it is to stay on top of the changes and curate them all into a convenient, performant, reliable, and coherent platform.

Meteor 1.0/1.1 shows that they have great taste. 1.2 shows that they are adapting to the shifting landscape (perhaps not quickly enough). All of it—from Blaze to DDP to Galaxy—shows that they they have the muscle to go deep and build out any piece they need to in order to uphold their vision.

If 1.3 and beyond can thread the needle of incorporating new technologies without leaving existing users completely in the cold, I think it will be a real achievement. In that light it doesn’t surprise me that MDG is taking their time to figure out thoughtful response.

3 Likes

I guess I don’t see the problem with having more choice in view layers. Could you elaborate more on this? I feel like i’m missing a valid viewpoint.

My outlook is that having 2 view libraries to choose from means you’ll have more packages in atmosphere. One might be for Blaze UI’s and another might be for React UIs.

For the packages that do things other than views… for example a Router, the package author just needs to de-couple the view from what they’re doing. FlowRouter and simple:rest are a great examples of decoupled packages!

The next version of Autoforms could do this too by building the core that is ignorant to the kind of UI then create two adapters for each view library. Then you end up with modular packages like autoform-core, autoform-adapter-react, autoform-adapter-blaze.

To make the user experience better provide a wrapper package like autoform-react and autoform-blaze to make installing these easier (as it could require many sub packages).

I honestly think the choice of view layers will strengthen the community by forcing developers to think modular and build composable packages going forward.

4 Likes

I love where you’re going with this. Meteor packages will need to decouple themselves away from the presentation layer. It is clear that Meteor is moving towards a “pick your own presentation layer” paradigm. If the community doesn’t abstract packages from their presentation logic, then we will be drawing lines between sets of packages that we can and can’t use.

For autoform, @aldeed has already said that React support is not on the roadmap. He doesn’t seem to convinced that people will stick with React. Fortunately, since collection2 and simpleschema are cleanly abstracted from the presentation layer, and the majority of autoform is Blaze templates, it could be easily ported to React (with enough time and resources). But then you’d also have to port all the autoform add-ons written in Blaze.

2 Likes