SIDEBURNS UPDATE: Rendering Blaze 1 to React is looking 100% possible


#1

First of all it’s now called “Blaze React”: https://github.com/timbrandin/blaze-react

…One of the biggest challenges, was event handlers.

In short, the transpiler would end up generating code like this:

class ParentComponent extends React.Component {
   'click .some-class'( ) {   }
}

So real quick, the idea here is that your current event handlers become methods on a component. You can decipher the difference between them and other methods because they start with an event name and then a space. From there we need to essentially do the equivalent of jQuery work on a string to attach the event handlers to element attributes like you statically do in React. That’s easy on a per component basis.

The Challenge is this: In Blaze world, these event handlers can select child components. Which means the selectors of all parent components must be accumulated, and the entire component tree must be rendered to an HTML string to be able to look at the class names, ids, etc, associated with elements, which are dynamically generated at runtime and often change (eg: an active class).

So after lots of trial and error I finally have a workable strategy:

  1. pass down and accumulate all event handlers from parent component to child component dynamically at render time
  2. in componentWillUpdate render the entire tree to a string using React.renderToStaticMarkup to a variable, parse it and for the HTML corresponding to the current component, statically attach within the string the matching event handler as an element event attribute. This was the challenging part–i.e. accumulating the entire tree of HTML to matchup selectors since selectors are scoped to all child components in Blaze, not just the current component.
  3. bind the current component’s props as the context of this handler, which may have come from a parent component.
  4. bind this.props as the data context, and pass in the component which is also the value of this in the render method as one of the parameters (and the event object, modified to look and operate like the one passed by Blaze).
  5. do the same on the next component rendered, and so on. END

There are many other parts, but this has been the one that has eluded me for the longest, and likely the last one. Here are some highlights of the past main triumphs:

  • component methods–like Blaze helpers–track reactive dependencies and call component.forceUpdate() instead of having to freeze it all within the single getMeteorData method. It’s ultimately the same thing, just more automated–and basically identical to what you do already in Blaze. That’s the “Deep Tracker Integration” alluded to by @gschmidt yesterday. That by the way is “The Soul” of Meteor, and what was absent in the previous Meteor React implementation launched over the summer.
  • binding props as the context to methods used as helpers.
  • pass the component down as a parent to child components: <div parent={this]></div> so that children can do: Template.parentLevel(level) and in spacebars: ../../ etc. You can climb the entire tree backwards, and its data will always be in sync for you. No need for forceUpdate. An example of this is in the jsfiddle below–you will like it.

Sideburns, now called “Blaze React” was in a state of flux because we weren’t sure how to accomplish some of the more dynamic aspects of the whole thing. We had the transpiler of spacebars code for the most part, and @timbrandin did some great work to bind props as the context for helper methods, which themselves were able to run in an autorun, but we still had some major hurdles we were unsure about.

So in short, it’s looking like Blaze 1 will be completely capable of using React as a renderer. Sure, some of this may take a hit on performance, but the idea here is to look at it as a stop gap while Blaze packages eventually catches up to the super cool Components-based one MDG is now building.

That said, I think MDG should make this a first class aspect of their project here. I can do it, but this is really a pre-requisite of what they are doing, and if done together they will be done correctly. Being done “correctly” will mean there is a clear upgrade path from the Blaze 1 React Renderer to the Blaze 2 Components React Renderer, or whatever it ends up being called. There should be an option to turn the transpiler off as a build step and permanently transpile your code to the best form of the your code that can be generated in React. From there you don’t need to use the transpiler anymore if you choose. The idea being you have been passed the ball to easily make the changes that undo the things that take a hit on performance here and do them manually in the faster standards-based way. …And, again, to start, you can use the transpiler as a build step until you’re ready to make the leap. That’s the vision I have for this whole thing.

Anyway, that’s all for now. I wrote a lot more in depth in the yesterday’s Blaze post. You guys should all read it, since you’re all very concerned about this. Here’s a quick jsfiddle showcasing accessing parent components, the React “Context” feature, and most importantly: dynamically attaching events at render time by converting the return of render method to a string of HTML, then attaching events primitively using jQuery, then transforming the HTML to a React element (the proper return of a React render method) as if it was JSX, and finally returning it from the render method with all the events attached from our Blaze style event maps; that proves it’s possible to render the entire DOM at any given moment of time, and properly attach events taking into the consideration selectors across the entire DOM tree!

http://jsfiddle.net/s5823g2w/

Play with it, you will see a lot of what Blaze does isn’t like “against the React way” or something. The entire interface and expected behavior can be mirrored to React.

MDG would be remiss not to invest heavily in this option for their users if they are at the same time serious about prioritizing React. The path forward can combine the best of both worlds to make the path seamless going forward. @evanyou looking forward to discussing this with you when you have time.


#2

My first response is why? The blaze ideology should die. Hard to reason data context, constant time debugging our templates with no real testing story, and just decrease in developer happiness when building large apps. Keeping this syntax and ideology alive is not the right way to go. It probably makes sense for the short term but anyone thinking that these problems are acceptable…man I don’t know what to say. I think the sideburns project should not repeat the blaze mistakes as we need to solve blaze’s issues…not just make it so you can write react under the hood…


#3

People’s livelihood is at stake. All their packages will be essentially obsolete overnight and require many hours of rewriting. Our customers’ applications won’t get the bug fixes they need because react-based stuff will be triaged as priority. It’s not good news for people who have based their life on meteor, the same people that built all the packages you’re using today.

…MDG is already doing what you are saying.


#4

Yeah, add Blaze and Jquery to React. Why?

React doesn’t need Blaze or Jquery, it can do everything without them. Use React components, there are already many libraries like in Jquery. You don’t need to mix these 3 things just because you don’t like syntax or don’t want to explore components.


#5

I think that Blaze 1/2 will be compatible. For example the entire atmosphere won’t be closed out from you, you’ll just need to use a different import syntax (or something) to insert, say a TabularTable into a Blaze 2 app.

Blaze 2 packages can just pop into Blaze 1 with a blaze helper.

The only reason i’m confident on this is because i’m doing the same exact thing today (with React instead of Blaze2) for apps that people’s livelihood depend on. I can use the entire Atmosphere at a whim. Blaze users can also import React components as well.

That being said, I think Sideburns is an awesome project.


#6

The syntax you’re talking about–taking from the React tutorial on the Meteor site–is:

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" />;
  }
});

So that means developers have to do this in their application client code, OR all the package authors have to make react versions of their code that already do this. Thats provided it works for anything but the most trivial use cases, and doesn’t require basically sideburns style transpiling of its own. Also, changes to packages won’t necessarily play nice with inter-opting packages that haven’t done it yet. That creates a mess of dependencies and incompatibilities–when many packages are dependent on each other, which they are.

As for the above example, the Template.loginButtons example doesn’t take any parameters, i.e. isn’t passed any props. Then you need to do Blaze.renderWithData(Template.loginButtons, this.props, React.findDOMNode(this.refs.container)).

Then what if you are template uses “custom block helpers.” There’s no standards-based way to implement that. Blaze has no feature: Template.renderWithDataAndContentBlockAndElseBlock. React has this.props.children which is the equivalent of the contentBlock, but there’s no equivalent of elseBlock. The most used packages make use of this dynamicism a lot to provide clean abstractions to application developers, the best example being @aldeed 's AutoForm.

Basically, doing anything beyond trivial stuff will likely take major work on either the package author’s part to provide a different version of their package, or application developers’ parts, if the package/component is in fact able to be composed in such a way.

In addition, it means you must ship both Blaze and React to the client. There’s probably many other pitfalls not discovered yet.

A robust solution that goes at the root will certainly save more time than all our collective work, and by several orders of magnitude. If it can be done, and it’s not that insane (which it isn’t), there’s no reason it shouldn’t be done, and officially by MDG. I’m just trying to make sure MDG really explores this. It’s a non-standard solution to a degree, but that’s what stop-gaps are, and it will work. So with everyone complaining about their dealing with Blaze/React in 200 post long threads, and not speaking up and the few that have moved on to pure React land, like yourself, downplaying it, it’s a problem.

The goal is to get hip to the possibilities, the similarities between React and Blaze. It’s not as different as you might think. It’s hard to describe what the problem is with Blaze–you and I have tried, and basically failed. It just gets tangled, and it’s hard to pinpoint why. The reason isn’t that you can access parent data context or state–you can do that in React very easily, and without losing the ability for child components to update properly; just think about it, you already have the parent props/state, it’s just a matter of passing more of it down to children, which can be done automatically for you; just check the above jsfiddle link in the initial post–the problem is the timer/flush based stuff that Tracker/Blaze does where when too many things are going on it combusts. I.e. where timer callbacks get missed. And it’s likely just oversealous autoruns, all just constantly triggering, even if they aren’t causing a cycle. With their current proposals, we are just going to get more Tracker. Perhaps the problem is Tracker, not Blaze. If tracker is going hey wire doing a million things, it’s going to cause React to constantly re-render too. React isn’t that fine-grained at rendering. It could even be less than Blaze in terms of the pre-virtual-dom updates.

The virtual dom functions as a filter for React, likely doing the absolute most fine-grained dom updates possible. Blaze is likely doing a lot more. That’s where performance counts the most. Perhaps React touches the actual DOM less, but touches the virtual DOM more than Blaze does the equivalent. The latter doesn’t matter nearly as much. In reality, React likely touches the real DOM less AND it has less going on pre dom manipulation. Blaze has tracker going hey-wire pre-virtual dom.

So what I’m saying is this: to blame it all on the flow of data is a cop out on MDG’s part. The “unidirectional” thing does not need to mean developers don’t have an abstraction for them to access parent component data/state parent.

Tracker is a menace. To me, given it simplicity to application developers, a welcome menace though–one that’s optimized primarily by application developers tuning it, e.g: with cursors that only select the fields you need. I do think, though, more specific types of Autoruns could be offered as I provided examples for the other day. They would help box in the variables that trigger Tracker-based reactivity.

We’ll likely be stuck with many of the same issues as long as Tracker is being used–at least without additional types of autoruns that offer fine-grained control and lead you into the “pit of success” of reactivity. Not recognizing the real problem here is going to lead to half-assed output: A) unnecessarily losing Blaze features (its simpler interface) and B) doubling-down on Tracker, which is what might need to be replaced altogether, or at least severely upgraded–that’s where resources may be better spent. So by pretending and sweeping things under the rug, we aren’t truly solving our problems here. But we can at least hope React is a panacea.

Autoruns were a nifty concept. Extremely clever. And the interface for application developers is super duper seamless and natural. It’s the soul of Meteor, and what got us all hooked in the first place. So hopefully the interface doesn’t need to become complex through requiring it to be more explicit, but the implementation may be in dire need of an overhaul. Or perhaps it was just wishful thinking all along. I think MDG has likely found themselves married to tracker at all levels of the stack, and don’t want to face its core performance-oriented pitfalls. Who would? I wouldn’t. Nobody wants to give up a great interface–which to be clear, it is–for more implementation non-sense we all gotta do. But maybe there is a middle way. Outside the box solutions include: Autorun classes that let you define how they will perform + a library of pre-made ones to choose from, a UI in your application to inspect the behavior of all your autoruns (what fields are triggering them, what collections, where in the code, what other autorun they triggered after they run, where they are defined etc). Basically autoruns suffer the “lack of observability” core problem of software programming Bret Victor pointed out several years ago. Deeper insight into our autoruns could be all we need. There could straightup be alerts in this GUI that warn you when too much is going on and you should optimize it, and provides optimization recommendations.

If you’re application’s autoruns weren’t so busy, I’m willing to bet we wouldn’t have any of these problems. So the root question should be: how do we box autoruns in? Not: how do we put them up against a more resilient rendering system, which is what we are doing.

The problem is that the concept of “cursors” wasn’t taken to the property level. Blaze attempts to re-render the DOM even if you don’t use properties on the models you’re rendering. Most developers aren’t optimizing the fields in their find() options. That means if you have a cron task on the server, for example calculating aggregates, all your clients are going to attempt to re-render model objects that didn’t even implement the field that changed. There’s no reason the properties of objects could also be the equivalent of “cursors,” i.e. they only trigger Tracker-based reactivity if they are used. That in fact is how Spacebars already deals with Cursors. Spacebars does less work rendering a cursor in an #each loop than arrays. Conversely, we shouldn’t have to use .fetch on our cursors in javascript code. Spacebars should just be aware its a cursor, whereas standard javascript code return an array. So take that concept to the property level, and they will only trigger reactivity if Spacebars encounters them. And in javascript, you get the standard strings and integers you expect as returns. That way the application’s performance is dependent on developers taking on all this unnecessary code to specify fields to all their finders. The problem with that is the fields you actually require is always changing–it’s just easier to omit the fields specification. And that’s what everyone does–until they get into this tangled mess, or if time provides, and the project is continuing to pay for itself, at the end of the project.

Basically having to specify all the props you pass down from component to child component in React has 1-to-1 parity with specifying fields in a in a find() call. Deeply integrating Tracker into React won’t solve that.

Which is why I’m suggesting we get to the root of the matter here before jumping to conclusions that Blaze needs to be thrown out the window. Also, as far as I’m concerned, I don’t care about Blaze. It’s an interface. I like its interface, specifically the components-based version, such as @mitar’s Blaze Components. Whatever is going on behind the scenes doesn’t matter to me, and can be optimized. The data flow in Blaze is the same as React, or at least with some tweaks can match up to React and not break its unidirectional immutable rules. Spacebars, plus defining event handlers separately, plus an enhanced way to specify helpers + events on parent components to manage state in one top level place (rather than do all the busy work to pass them down) is what I want. The latter of which, the current Blaze or @mitar’s Blaze Components doesn’t offer, but is what I’m working on. If you can specify both the helpers and event handlers on a parent component, but they can be used on child components while utilizing the state of a Parent component is a winning concept, specifically if you also have access to the child component’s props data context. React has you do a lot of busy work to pass all this stuff down. It’s not just data, it’s methods! That’s ridiculous. It may feel good now, but it’s no good. Cluttering up all your template code, which should otherwise be readable by a web designer with functions, and then continually passing them down–that’s not a good interface. It’s not separation of concerns by any means. It’s a stop gap to access state in its proper place because they couldn’t think of a better solution.

So anyway, MDG’s likely current plan for the future of React + Tracker won’t solve problems inherited from Tracker unless we get to the bottom of it, which is “property-level cursors.” And the messed up part, it likely is easier for them to solve that with Spacebars. In JSX, it’s plain JS–they will have to make TJSX which is able to determine what’s a cursor and what’s not. “T” is for “Tracker” obviously. They need a step to be able to examine what a symbol is before rendering it, and if it’s a property “cursor,” register the component as reactively dependent on that property, but then spit out the value it contains. Again, you can’t do that nearly as easy in JS or JSX. I don’t put anything beyond MDG–they’ve accomplished wonders with Meteor, which is why I’m here–so I at the very least hope they are thinking about building their own JSX transpiler that handles this. It’s a natural progression from automatic fine-grained reactivity where you don’t need getMeteorData anymore, and component methods themselves track their reactive dependencies, TO where the same applies for the properties of model objects. My assumption has been they are considering the former. So they need to take it all the way, down to the property level. Double down on Tracker for real for real, MDG! We do love Tracker, it just needs to be better. And now you have more problems since you gotta do all this in the React world you’re not familiar with. I agree it’s the right decision to go to React, especially since Blaze can completely transpile to it, but you gotta handle the root of the problem while you’re at it.


#7

Can you provide an example package that I can test? Would autoform be one? You’r going to have to ‘quarantine’ an area in Blaze2/React and then use Blaze 1 inside this area… from here you can use autoform like normal.


#8

Yea I’d say AutoForm is the canonical example here–since the interface to the application developer is a custom block helper. Well, there is also quickForm which is used as a standard Template, but all real projects use the block helper form. Here’s the first example of it from its readme:

<template name="insertBookForm">
  {{#autoForm collection="Books" id="insertBookForm" type="insert"}}
    <fieldset>
      <legend>Add a Book</legend>
      {{> afQuickField name='title'}}
      {{> afQuickField name='author'}}
      {{> afQuickField name='summary' rows=6}}
      {{> afQuickField name='copies'}}
      {{> afQuickField name='lastCheckedOut'}}
    </fieldset>
    <button type="submit" class="btn btn-primary">Insert</button>
  {{/autoForm}}
</template>

I’ve never had the need for it, but you probably can do forms within forms for sub documents. And there may be other block helpers that I’m not digging up from the page right now that when used within each other may pose more problems. For sure, if there is custom block helper nesting, this is gonna be something @aldeed will have to do to not make it a bunch of hurdles application developers have to deal with.

I’d love to see how the basic #autoForm example above turns out. I’m sure you will figure it out, i.e. with this.props.children et al, and I’ll concede if that’s the extent of the complexity needed for 80% of all packages, than I may be making a bigger deal out of this than it actually is. It still will be a while before our codebase isn’t cluttered with a million blaze wrappers as stop gaps.

ps. @SkinnyGeek1010 check out what I just added to my previous post. Thinking through these things I just discovered what I think is at the core of the performance pitfalls Blaze/Tracker lacks compared to React! The summary is that specifying fields in a find() options object is the equivalent of specifying which props you pass down from component to child component in React. Blaze goes hey wire because everyone just leaves the fields option empty, and rightfully so for most of development. It’s a pain to constantly be specifying those props, and even more so in React since you have to do that for every child component they trickle down through. What I’m proposing is “property level cursors” (only within templates) that only trigger Tracker-based reactivity if the props are actually used! Similar to collection cursors. That’s the sorta automation React could only dream to have. Anyway, just think about it, Blaze attempts to re-render the DOM for all connected clients every time a cron task updates a field on a collection’s model object, say if various aggregates are being calculated and stored on a single field. Tracker should be silent, but it doesn’t know what properties are being used and which one’s arent. Now, I don’t love Blaze–MDG should do this in React with their own JSX compiler that detects what’s going on within render calls; or perhaps they do it in their Spacebars variant.


#9

Blaze Components do offer having state at the top-level component and using that state. You just get a reference to it and use it. It is just how you structure your component tree. Blaze Components do not require or predefine any of that, but it also allows and empower you to choose whichever you prefer.

See this ticket for more information.


#10

I’m not seeing. How can yo do the equivalent of this:

<template name="ParentComponent">
    <h1>SELECTED CHILD: {{selectedChildName}}</h1>
    {{#each posts}}
        {{> ChildComponent}}
    {{/each}}
</template>

<template name="ChildComponent">
    <button />
</template>


class ParentComponent extends BlazeComponent {
   onCreated: function() {
       this.name = new ReactiveField('n/a'); //aka in react: `this.setState({name: 'n/a'});`
   }
   selectedChildName: function() {
         return this.name();
   },
   
    events() {
      return super.events().concat({
         'click button': this.onClick
      });
  }

  onClick() {
     //event is triggered on child, but state is the parents.
     //the data context is the child component's. 
     this.name(??this.name??); // `this.name` usually is the data context, where is it?
  }
}

Can yo do that? Will that work?


#11

I assume you can–my next question is can your helpers work in the Child component? That’s key for the combination of event handlers + helpers to be effective in like 50% of all scenarios. For example (and this time I won’t use ReactiveField since I’m not sure where the Data context is):

<template name="ParentComponent">
    <button class="expand-collapse" />

    {{#each posts}}
        {{> ChildComponent}}
    {{/each}}
</template>

<template name="ChildComponent">
   {{#if expanded}}
       <h1>{{title}}</h1>
       <p>{{body}}</p>
   {{else}}
         <h1>{{title}}</h1>
   {{/if}
</template>
class ParentComponent extends BlazeComponent {
   onCreated: function() {
       Template.instance().expandedIds == new ReactiveVar([ ]); 
   }

   
  events() {
     return super.events().concat({
        'click button': this.triggerExpandCollapseAll,
        'click h1': this.triggerExpandCollapseOne
     });
  }

  //helper on parent
  posts() {
     return Posts.find();
  }

  //helper on child!! ..the point is it needs to share the state of the parent.
  expanded() {
     let ids = Template.instance().expandedIds.get(), // in my "Blaze Components" I have simply: `this.get('expandedIds')` since `Template.instance()` won't work.
     return _.contains(ids, this._id);
  }

  triggerExpandCollapseAll() {
     let ids = Template.instance().expandedIds.get(),
         allIds = this.posts().map(post => post._id); 

     if(_.isEmpty(ids)) Template.instance().expandedIds.set(allIds);
     else Template.instance().expandedIds.set([ ]);

  }
  triggerExpandCollapseOne() {
     let ids = Template.instance().expandedIds.get(); 

     if(!_.contains(ids, this._id) ids.push(this._id);
     else ids = _.without(ids, this._id);

     Template.instance().expandedIds.set(ids);
  }
}

So this is how my Blaze Components works–helpers are inherited automatically (and dynamically) by child views at render time, while keeping the state from where they were defined. In that example, the state is needed on not just the parent, but also the child. And specifically within helpers. So that means the helpers must be automatically inherited by children. I checked your lookup.js file, and you don’t check the chain of parents for helpers that match–though, there could be other ways to accomplish this.


"Deep Tracker Integration" for React Today!
#12

Event in the first example will trigger on the parent component. But in this case the code in Blaze Components would be:

onClick(event) {
  this.name(this.currentData().name);
}

#13

got it!

…i figured that must be possible with yours. what about the second? how do you propose state is shared between parents and descendants when it comes to helpers?


#14

Again, Blaze Components are trying to provide minimal API to allow you to have then your preferred way implemented on top. Even I do not use bare base class myself, but have extension build on top of it.

So the design decision was to not provide such automatic inheritance because some people would not like it. But it is easy to add it if you want. Or easy to create methods to allow you to do that.

How I would do it is implement on child component the expanded helper:

expanded() {
  let ids = this.callAncestorWith('expandedIds');
  return _.contains(ids, this._id);
}

(Here I assume use of ReactiveField.)

How I see it is that ParentComponent provides public API expandedIds, which then child components use for their need.

This allows much more modular code. Why would expanded be somewhere else, when it should be with the component which uses it.


#15

That’s not a bad approach. My questions are:

  1. what if you re-arrange the templates, and nest your code in more child templates–how does it know which ancestor to reach up to?
  2. suppose it’s programmed to reach the first ancestor that has the ‘expanedIds’ key (solving problem #1), what if it collides with other properties? I’d say this is sub-par compared to helpers maybe colliding: since helpers are static and more obvious since they are usually static members of a class prototype, whereas state keys can be dynamic–who knows what keys are dynamically added at run time? State keys are also more buried in code.

So, if we are just to accept #2 and #1 is solved by your system, here are the pros and cons of both approaches:

CALL ANCESTOR APPROACH:
PROS:

  • helper with component that uses it.

CONS:

  • more potential for inability to access parent state
  • extra call to get access to state. it’s available by default.

HELPER DEFINED ON PARENT APPROACH
PROS:

  • helper with component that implements other methods related to the state! (it’s subjective)
  • matches the React pattern of passing functions down–just automatically in this case
  • enforces “state management strategy”–helps you think about where you want state to live, and unlike React makes it very easy to pick that place, and just start writing your methods there, rather than passing them down, or in case of the “ancestor” approach reaching backwards.

CONS:

  • may require more time to wrap your head around, just as passing functions down does in React. For some it’s more natural to put methods on the child component and then ask for state than to consider state as a given and pass your helpers down

The main thing though–is you aren’t “passing your helpers down.” That’s React’s problem. In the “Helper Defined Parent on Parent Approach” above it’s automatic. It’s natural dynamic inheritance or whatever you wanna call it. So it’s a given that all work you previously did on parent components you have available to child components (i.e. their helpers and event handlers). That’s automating lot for developers, while of course guiding developers seamlessly into the state management pit of success. Whereas in the first approach, state management ends up being an after thought. You are more likely in that one to be in the scenario where you now need to move your state up a level so the parent can have an “expand/collapse all” button, whereas in the second approach, you are likely to have already defined your state-related methods on the correct parent. The reason is because templates get dumber and dumber the deeper they are nesting–we are used to treating higher up components like controllers anyway. In addition, you start by coding parent templates/components. You will likely first implement many methods on these higher templates in their first form before moving down to child templates. It’s not only very nice to have those methods available to you as compose in smaller and smaller nested templates, but the state is also very often already where it needs to be! I.e. the perfect parent component who is the first component that composes all the children that access the state.

And here’s the clincher: basically, if you continue to compose your templates of smaller and smaller nested templates, which is a common refactoring process, then you need to move your helpers too. You never have to do that with the helper inheritance approach. Compose into smaller more organized nested templates all you want! I do this all the time. I have many dumb templates, which I make so the messier parent templates become cleaner and easier to reason about again. But if you do this with the call the ancestor approach, you have to go back and change the js for where its helpers are defined as well. You have to assign them to a different template now, perhaps move them to another file. And there’s more: events are already “inherited” from parent to child component. So it’s a mental mismatch between how helpers and events work. If they both worked the same, that’s one less thing developers have to factor into their reasoning. And that’s where the perfect marriage of the “Helper Inheritance” approach occurs–they both behave the same way and channel state to the perfect parent, while automatically doing everything else for you, solving the problems of both React and Blaze (fireworks, shooting stars, etc). I’m not gonna go back and change the pros/cons list above, but there are now many more in favor of the helper inheritance approach.

It’s basically just like looking at React the first time, and being frustrated. It’s a paradigm shift–though here to a lot lesser extent–but once you get over the first hump, you begin to see all the additional benefits. I know you were trying to stay more true to standard Blaze–I love your Blaze Components. That was a good strategy to make it more seamless to move over for existing projects, and a big inspiration for what I’ve been formulating. The route I have taken doesn’t care as much. It’s for new projects. So I’m just trying to figure out the absolute best path forward, having spent a lot of time having completely fixed Blaze (even if it’s too far of a departure from Blaze for old projects), and now trying to factor in how the React fiasco fits. Fortunately, it’s shaping up quite nice. The “Helper Inheritance” interface I already have is matching up perfectly with React.

Here’s the revised version of the previous class in my style fyi:

class ParentComponent extends Meteor.Component {//onCreated not necessary
  posts() {  //helper on parent
     return Posts.find();
  }

  //helper on child!! ..the point is it needs to share the state of the parent.
  expanded() {
     return _.contains(this.get('expandedIds'), this._id);
  }

 'click button'() {
     let ids = this.get('expandedIds'), //basically `get/set` handles state behind the scenes (i.e. `Template.instance()`
         allIds = this.posts().map(post => post._id); 

     if(_.isEmpty(ids)) this.set('expandedIds', allIds);
     else this.set('expandedIds', [ ]);

  }
 'click h1'() {
     let ids = this.get('expandedIds'); 

     if(!_.contains(ids, this._id) ids.push(this._id);
     else ids = _.without(ids, this._id);

     this.set('expandedIds', ids);
  }
}

#16

For a developer who is developing for developing sake, there might be no reason. But for the rest of us, who have invested in the Blaze technology and want to focus on business value, any smooth upgrade path is very welcome. So I am glad that @faceyspacey is developing this.


#17

I don’t get it, you can replace your app one small part (as small as a button) at a time and slowly refactor over time. You really just cannot sit down for 10 min (not even) and learn react?


#18

The callAncestorWith searches for the first ancestor with expandedIds property. You can safely reorganize the code. You can also decide that you have all the state in the top-level component.

You can also imagine some other ways to traverse the tree and search for wanted component instance. For example, one other common pattern I am using is to search for an ancestor component instance which is an instance of a particular component class.

suppose it’s programmed to reach the first ancestor that has the
’expanedIds’ key (solving problem #1), what if it collides with other
properties? I’d say this is sub-par compared to helpers maybe colliding:
since helpers are static and more obvious since they are usually static
members of a class prototype, whereas state keys can be dynamic–who
knows what keys are dynamically added at run time? State keys are also
more buried in code.

That is the question of your code structure. You could have your public API of your component be attached to this.public. You could have all dynamic properties which should not be public prefixed with _. It is really left to you to decide how you want to do that.

I think it is of similar complexity to how to assure that components do not clobber its own helpers when you automatically inherit them through the components tree. I prefer explicit traversal of the tree though (because you can control it more) than one where you automatically inherit and the only condition on which you can match is symbol/property name.

Again, it is on the developer to decide how they want to use that. They can put all helpers on the top level and then know nothing will be clobbered. Or they might even want to clobber something on purpose (to override/extend it).

What I am saying that you can with Blaze Components decide for both approaches, as you prefer.


#19

I see how you can move state to controller parent. Just now, you gotta explicitly call to your ancestors every time you need some help, but I get it. With stuff like calling an ancestor of a specific type, it’s a little bit complex, but pretty solid. I. Both at the end of the day are very similar. My goal was just to fully understand how your system works, its intent, and the best way to use it. I understand it now. I mean basically Meteor developers needed to be doing this sorta stuff for a long while now. And we need to insure the new component architecture put forth by MDG (or our wrappers) takes into consideration the ideas we’ve been throwing around. We both have our preferences, but I’ll tell you this is for sure: my preference is not passing down every damn event handler function and helper method return to child components like in React! It’s unnecessary and can and should be automated. It doesn’t violate the unidirectional flow. It only slightly violates immutability–since you need references to parents. But in the most harmless way possible. That reference behaves like immutable data flowing through the system–i.e. it changes to a new reference when the parent changes. MDG won’t have the balls to modify React that much is my guess, but someone’s gonna have to if we want this done right.


#20

whats your plan? a react component inside a blaze component inside a react component inside a blaze component?? You want to write wrappers like the above loginButtons one every step of the way for the next 1.5 years? The problem is bigger than having to “learn react.” I know React very well, and this is a major problem for me given how much Blaze code across many projects I have. How many Meteor projects in production do you have? I can barely imagine even going in there putting some react code in the midst of all the Blaze code. …I mean where do you start? You basically should start with React as the wrapper for everything–so then, what, you render your entire Blaze app in React. Where do you start eating away at the Blaze? Picture a basic website, one column is blaze and the other is react, while react contains them both??

There’s no answers to these questions. It’s not pretty like you think. And for sites already in production that you’re now ripping apart, and throwing the same stuff up together in hacked form to get access to like one React thing. It basically makes no sense, and the reality is the majority of these projects will never make it to the next React round. React Meteor will be for new projects. And that’s just gonna be the reality for the vast majority of cases. You’re either Blaze or React. Doing both isn’t gonna be a common practice, my friend. The only time it will happen is in new React projects that want to cherry pick one or 2 legacy Blaze packages like the login buttons. That’s it my friend. IN SHORT: NEW PROJECTS. I’d go study and use Phoenix for the next 8 months until Atmosphere isn’t package poor in terms of components compatible with React.

There’s a plethora of amazing stuff for Blaze, specifically for Blaze coupled to Meteor that won’t exist for a long time for Meteor + React, even if it’s just minor wrappers that must be implemented. If you can start out on a fresh tinker project with React + Meteor, it’s not as much of a problem. U will still find yourself needlessly writing wrappers for what was simple plugins–not a good feeling! However, for your troubles you may now get access to the React ecosystem, but you have to understand these packages are just view packages. Packages that couple the entire Meteor stack are a lot more powerful. We want these packages. They exist already. They will all have to be upgraded. In practical terms, professional developers are gonna stay away from all this mixing and matching. Sending both React and Blaze libraries to the client–that’s jus the beginning of the troubles. We are all gonna veer away from this option, and cheery pick as last resorts.