Is React really "foolproof" for Meteor right now?


#1

<pun>If you mean “foolproof” because of JSX, then yes!</pun>

It looks like MDG plans to revise the Tracker integration - and we know they’re eyeballing GraphQL and React Native integration. This could mean there will be a new way data layer on the way. So even if we write our applications in React, isn’t there a reasonable risk that we would have re-write the data part?

If so, it sounds like MDG needs to rush to get this out, and then we can talk about Blaze 2 (and I would imagine that is what’s holding it back right now). I figured I’d start this as a separate thread to give everyone a heads up. And I figured it’s better we get this out of the way now before another thread pops up and steals all the hype from my Star Wars discussion.


Next steps on Blaze and the view layer
#2

@tmeasday shared an interesting argument for Blaze over at this thread

Although starting a project in pure React seems like a pretty foolproof way to avoid any transition issues (and that’s what @sashko’s said), I think there are plenty of reasons why you might not want to do that (either due to the issues that the Templates+React project aims to solve, or simply that you want to use all the existing Blaze goodness that’s out there).

He also adds

I know the lack of complete clarity right now is frustrating, and I’m hopeful that some things will be clarified further very soon!

Which I think is the main point. Although I love Blaze and kind of not like React, I can go whichever way is foolproof or futureproof. All I’m seeking is stability and support which is what clarity kind of implies.

And now that there are two core devs working on the official guide going full on Blaze, but one actually advises going with React, I believe @gschmidt or @evanyou should at least provide us with a clearer picture of the future, including some ballpark estimates around delivery dates.

Don’t know, I’m kind of both happy and torn by Tom’s comment.


New package: mdg:validated-method - Meteor methods with better scoping, argument checking, and good defaults
#3

I hope it’ll make ~3-4 months to land the new Blaze or more.

GraphQL or a fresh look at Meteor’s data layer takes another more time. So, the question is can you wait until all that happens?


#4

Well that’s the dilemma, isn’t it.

Obviously, I can’t wait 3-4 months to start a new project.

And since I have an “enteprise-project” at hand (read: stability, predictability, long term support)

  • I can’t start with Blaze if it is going to be obsolete in less than 2 years.
  • I can’t start with React if there is no “meteorish” forms component library out there

#5

Write the application in COBOL with 3270 for the view layer or RPG/IV with 5250 for the view layer. Instant foolproof and long term support.

Those two combinations will still be here for another 50 years while other technologies come and go like leaves blowing in the wind.


#6

From this what I can see is, try to build a your own Form component in React + Meteor if that’s the only obstacle for using React.


#7

This is the same dilemma that I am in right now. The confusion is on a mega scale. I have two ideas I would like to see through but I have stopped doing any more work on them for this reason. Heck I even started looking at Phoenix :frowning:


#8

try to build a your own Form component in React + Meteor

well, if one of the main reasons for a react crossover is to embrace the larger ecosytem, shouldn’t we instead try finding ways to better integrate with what’s already out there, instead of reinventing the whole stuff?

@jacobin

Write the application in COBOL … will still be here for another 50 years while other technologies come and go …

haha, it may actually be a viable alternative, but I’m not good at cobol. I’m actually quite the fortran master, though :smile:

But seriously though, stability is really very important for enterprises which I cannot stress enough. We should at least take cue from Java. There are API’s that log deprecation notices but still are available after 3-4 major versions and 4-5 years, and with very clear migration paths.


#9

How I feel right now. But I will not turn to the dark side like this guy!


#10

a lot of people are in the same boat as you are.


#11

well, if one of the main reasons for a react crossover is to embrace the larger ecosytem, shouldn’t we instead try finding ways to better integrate with what’s already out there, instead of reinventing the whole stuff?

It’s super easy to add Meteor functionality to existing React form components and use them in your own form solution (this is what React goes on about composing components). This allows you to easily use existing form libraries and add what your app needs.

Also if it’s an enterprise project the last thing you want is a highly coupled form library. Most likely your app is not a simple crud app and will need some kind of special form functionality.

I made my own internal form library that is composed from other React form components that allows custom validations and is super flexible (since we made it in house). It allows you to re-use generic functionality and add in your own.


#12

@SkinnyGeek1010 I’ve seen this before (in one of your other posts) and I must admit I am quite intrigued.

I was thinking along the lines of writing a parser for simple schema to convert them into whatever format the form library requires its own validation and then SS’s validation context again back to whatever the library requires for displaying errors. But then again, that seems too coupled with meteor and I have mixed feelings about that.

I’ve actually even considered applying for that job post you’ve shared a couple of days ago, you know, as some learning experience :smile:


#13

Here’s my suggestion, though take it with a grain of salt as I don’t have the entire picture of your project:

If you’d need an auto-forms example, it would be very trivial to implement a basic working version of their custom-insert-form. You would just need these pieces:

  • SimpleSchema (does heavy lifting)
  • Your own Form component
  • Your own Input component
  • function to insert data
  • function to update data

Then you can use the ‘functional’ way of doing things (you’ll find out that this goes with the grain with React). Each one of these is being passed into another component. Thinking about in the ‘Unix way’ will help.

For example, define a schema and save it to a module namespace. Pass the schema in as a prop into your new Form component (so it can use it later). Pass the insert function into the form component (again to use later). Next add the inputs into the Form as children (like the link example). The 'name' of the input can map to the key of the schema. On submit the form prevents the default submit and then gathers the data from each child input, validates it, if valid then it calls the insert/update function and passes in the form data as a param, and if you want to make it generic, the collection too. This function will take in the data and the collection as params and then it will make an insert with the data it’s given. Normal security rules apply.

This whole process makes it very easy to test because none of the parts really know about each other (well the form has to reach in and grab children but thats it).

The key is making it de-coupled and generic so that you can use it in any project, and you can pass in data as params to make it work on a per-project basis. This allows you to build up a toolbox of ‘react parts’ that can be used in future projects.

You can even swap out the insert/update projects depending on what kind of backend it is… Meteor/Java/Python/Elixir… the form library doesn’t care (and shouldn’t). You could bundle up all of these into AwesomeForms namespace so that it’s easy to use as a package.


#14

@msavin @arunoda

If we first had a subscription-based ORM for Mongo that you could then query/subscribe to from your components/templates, then using GraphQL would be no problem on, especially on the client. It’s not that hard to convert GraphQL queries to what their form in Mongo would be, specifically the form we would have if we also had a standard way to query relations (which are essentially the same as fragments). We could essentially future-proof our components by using GraphQL, which for now would be converted on the client to their “Mongo ORM” equivalents. I’d also build a React mixin that lets you define the associated GraphQL on the component:

class TeaStore extends React.Component {
  render() {
    return <ul>
      {this.props.store.teas.map(
        tea => <Tea tea={tea} />
      )}
    </ul>;
  }
}

TeaStore = Relay.createContainer(TeaStore, {
  fragments: {
    store: () => Relay.QL`
      fragment on Store {
        teas { ${Tea.getFragment('tea')} },
      }
    `,
  },
});

like this:

class TeaComponent extends React.Component {
  fragments: {
    store: () => Relay.QL`
      fragment on Store {
        teas { ${Tea.getFragment('tea')} },
      }
    `,
  },
  render() {
    return <ul>
      {this.props.store.teas.map(
        tea => <Tea tea={tea} />
      )}
    </ul>;
  }
}

That extra step of creating the Relay container separately is only needed if you want to attach it to multiple components. Not a bad decoupling feature, but a step not used in somewhere between 50-90% of components in typical projects.

So essentially what you have is a definition for a mongo query. It could also be described in plain mongo js like this:

class TeaComponent extends React.Component {
  subscriptions: [
    {model: Tea, name: 'all', with: ['teaInfo']} //tell you what `name` is next
  ],
  render() {
    return <ul>
      {this.props.Tea.all.map(
        tea => <Tea tea={tea} />
      )}
    </ul>;
  }
}

So, obviously on the server we’d have to define the GraphQL for what the Tea model’s all subscriptions looks like, or perhaps a simplified Mongo-only version that (when the GraphQL layer truly was put in place transpiles to GraphQL):

//server
class Tea extends Meteor.Model {
  subscriptions: {
     all: {
       selector: {},
       limit: 10
     }
  },
  relations: {
      teaInfo: {
         relation: 'has_one',
	 model: TeaInfo,
	 foreign_key: 'tea_id'
      }
  }
}
});

class TeaInfo extends Meteor.Model {
   defaults: {name: 'n/a', sleepingTime: 5}
}

That’s taking the Tea example from:

Of course the canonical Post/Comment version would look like this:

//server
class Post extends Meteor.Model {
  subscriptions: {
     all: {
       selector: {},
       limit: 10
     }
  },
  relations: {
      comments: {
         relation: 'has_many',
	 model: Comment,
	 foreign_key: 'post_id'
      }
  }
}
});

class Comment extends Meteor.Model {
   defaults: {message: 'nada'}
}


//and finally back to the PostsComponent:

class AllPostsComponent extends Meteor.Component { // ;)
  subscriptions: [
    {model: Post, name: 'all', with: ['comments']} //tell you what `name` is next
  ],
  render() {
    return <ul>
      {this.props.Post.all.map(
        post => <PostComponent post={post} />
      )}
    </ul>;
  }
}

finally, if you wanna use the GraphQL option, it would all look like this:

class AllPostsComponent extends Meteor.Component { // ;)
  fragments: {
    Post: () => Relay.QL`
        posts {
           title,
           body,
           comments {
              content
          }
       }
    `,
  },

  render() {
    return <ul>
      {this.props.Post.all.map(
        post => <PostComponent post={post} />
      )}
    </ul>;
  }
}

That’s without fragments from child components. I won’t show the Relay version with fragments–you can visit the above link on the Relay site for that, but there’s no reason we also couldn’t do that using the WAY SIMPLER MONGO JS version:

class AllPostsComponent extends Meteor.Component { // ;)
  subscriptions: [
    {model: Post, name: 'all', with: [CommentComponent.getRelation('Comment', 'some')]} 
  ],
  render() {
    return <div>
      {this.props.Post.all.map(
        post => <PostComponent post={post} />
      )}
    </div>;
  }
}


class PostComponent extends Meteor.Component { // ;)
  render() {
    return <div>
       <h1>this.props.post.title</h1>
       <p>this.props.post.body</p>
    
       <ul>
           {this.props.post.comments.map(
               comment => <CommentComponent comment={comment} />
           )}
    </ul>
    </div>;
  }
}

class CommentComponent extends Meteor.Component {
  subscriptions: [
    {model: Comment, name: 'some'} //as a fragment/relation, the subscription will automatically know to be joined by `post_id` -- the `name` shouldn't always be required, but it's here for familiarity
  ],
  render() {
    var {_id, message} = this.props.comment;
    return (
      <li key={_id}>
        {message}
      </li>
    );
  }
}

Anyway, as you can see, GraphQL is the super verbose option. My prediction is we will see lots of abstractions on top of it, not just on the adapter layer that interfaces with various DB options, but also on the query/mutation/subscription layer. That’s one of the key problems of jumping on the Relay/GraphQL bandwagon right now, and why I wouldn’t rely on this coming as quickly as React came (and by the way React has ben open source for almost 3 years, GraphQL and relay is like 1 year, less).

So anyway, using GraphQL directly is a downgrade from using the query language native to your DB. With “great power comes great responsibility.” GraphQL will effectively let you switch out the DB layer very easily. Just like using an ORM in traditional server side MVCs. However, those ORMs simplify a lot of queries. GraphQL effectively makes everything possible, so I’d say it needs an ORM on top of it for the 80% use case, which will shrink its API surface by to like 15%. I don’t wanna use GraphQL. I wanna use a simplified version of it that offers a subset of features. And then if I want the full capabilities, I have the option to use full on GraphQL–that’s the ideal case. As usual GraphQL is a lot of hype. You have to understand React’s aiming for supreme scalability. First and foremost.

If you look at what’s going on with subscriptions, for that to be incorporated as is, that would make subscribing/publishing in Meteor very time consuming. Something newb developers looking to prototype apps would be turned off by. Now, I’m not saying Meteor shouldn’t incorporate it. They definitely should incorporate its pattern of NOT relying on “Live Queries” and tailing the oplog like you, @msavin, pointed out a few weeks ago, i.e. where we should monitor writes before they hit the DB and then propagate those to subscriptions. I assume we’ll end up doing that. I’m not sure of all the challenges there–I assume it has to do with the question: “what happens if a write fails.” So we’ll need write failure resilience. But it seems like it will be doable. So that said, we could incorporate that without GraphQL, still providing an easy Mongo style interface to application developers. Meteor itself could and probably should use GraphQL to make all that work so they get access to an ecosystem of GraphQL tools to help them, but that still doesn’t mean that Meteor should require us to write all our publishers like this (taking from @arunoda’s Learn GraphQL example):

const Query = new GraphQLObjectType({
  name: "Queries",
  fields: {
    authors: {
      type: new GraphQLList(Author),
      resolve: function(rootValue, args, info) {
        let fields = {};
        let fieldASTs = info.fieldASTs;
        fieldASTs[0].selectionSet.selections.map(function(selection) {
          fields[selection.name.value] = 1;
        });
        return authorsCollection.find({}, fields).toArray();
      }
    }
  }
});

How is that any sort of improvement for Application developers. It’s not.

What about a mutation:

const Mutation = new GraphQLObjectType({
  name: "Mutations",
  fields: {
    createAuthor: {
      type: Author,
      args: {
        _id: {type: new GraphQLNonNull(GraphQLString)},
        name: {type: new GraphQLNonNull(GraphQLString)},
        twitterHandle: {type: GraphQLString}
      },
      resolve: function(rootValue, args) {
        let author = Object.assign({}, args);
        return authorsCollection.insert(author)
          .then(_ => author);
      }
    }
  }
});

Do we currently have to define how Collection.insert() operates. What about how we invoke them: No, we don’t. So defining mutation implementations is likely something we’ll never do ourselves–though MDG could end up doing it.

…How about how mutations are invoked:

mutation {
  createAuthor(
    _id: "john",
    name: "John Carter",
    twitterHandle: "@john"
  ) {
    _id
    name
  }
}

That’s not exactly an improvement over collection.insert(). I don’t see why our insert method couldn’t create a mutation to invoke under the hood though. But again, Relay/GraphQL mutations are not something I want to directly create myself. That said, I’m also someone that doesn’t use any of the Meteor API directly. I have an abstraction of my own for everything. Same with most developers that have been using Meteor for a while.

So, obviously these implementations should be handled for us by Meteor/Mongo. And that’s obvious for the Server-side query/mutation/subscription implementations. GraphQL is a protocol. It won’t come with a transport layer or implementation of subscriptions. The current system of subscriptions actually can be easily used. The point is MDG would be opened up to an ecosystem of helpful products to make all this easier to build, and build even better.

So I think we’ll end up using GraphQL to define similar queries to what you could define with a Mongo ORM (if it first existed) on your components. And I think we’ll end up being able to use GraphQL on the server to define what the corresponding descriptions should return, but my prediction for the server is we wouldn’t define query responses like this:

const Query = new GraphQLObjectType({
  name: 'BlogSchema',
  description: "Root of the Blog Schema",
  fields: () => ({
    echo: {
      type: GraphQLString,
      description: "Echo what you enter",
      args: {
        message: {type: GraphQLString}
      },
      resolve: function(root, {message}) {
        return `recieved ${message}`;
      }
    }
  })
});

Rather, we should define them with similar syntax we use to invoke them on the client:

class Post extends Meteor.Model {
     subscriptions: [
         Relay.QL`
            recentPosts(count: 10) {
                 title,
                 body,
                 comments() {
                    message
                 }
             }
        `,
       Relay.QL`
            popularPosts(count: 10) {
                 title,
                 body,
                 comments() {
                    message
                 }
              }
         `,
     ]
}
//something like that, but at this moment I'm not sure how we would specify 
//how the count is implemented, or sorts, etc
//BUT THIS IS EXACTLY WHY I'M SAYING GRAPHQL IS UNNECESSARILY COMPLEX.
//WE DESCRIBE THIS MORE EASILY WITH A MONGO QUERY SELECTOR + OPTIONS

So either way, there likely is a happy medium here. I’m probably not just experienced enough in GraphQL and Relay’s usage of it. But likely there is a subset we could use that closely mirrors Mongo selectors and options. And if not, we could make it and transpile down to full on GraphQL. In addition, we could extremely easily transpile Mongo selectors and options to GraphQL. We just need the subscription-based ORM built underneath it by Meteor or a package ;). Then easily a subset of GraphQL could convert back and forth with Mongo-style queries. And it wouldn’t really matter which you use. If you want the option to switch out DBs, subscription transport layers, and perhaps all of Meteor, then use the likely more verbose GraphQL proper. If you don’t need all that and simplicity of interface is more important to you, use the pure javascript mongo-based syntax. That too could be used for RethinkDB as well by being transpiled to GraphQL, and then having RethinkDB set as your DB (once that becomes fully compatible with Meteor, which it currently isn’t).


Now as for classes you see like Meteor.Model, we’ll save that for another day, but basically Meteor should go all OOP. I’m not talkin about implementation, let’s not have the functional vs classes debate. After all, React offers a Class-based interface, but is functional under the hood. It’s my opinion, and the opinion of many, that Classes still are and always be the easiest interface (side-effects and all) as it more closely mirrors human behavior/experience. Bottom line you mix and match both throughout a project, depending on scenarios. In other words: the “toolbox” argument.

…So anyway, we need real ways to define Models, and their constituent aspects: subscriptions, relations, aggregates permissions, etc. Meteor packages and application scalability problem (in terms of growing a large codebase) isn’t that we need a guide–it’s we need something that looks like Rails in the first place which dictates the standards by which we code our apps. The Docs at that point would be all the Guide most people need. It’s not currently that way because imperative/procedural style code implies so many ways it can be used. We need our code to look like our Rails code used to look, but the isomorphic version. Isomorphic Rails.

As it is, Meteor (and NPM) packages are a lot of imperative/procedural, um, let’s just say, complexity. The composition pattern of packages that took over from Classes used in the pre-Node world allowed for a a lot of dependability, in terms of interfacing between unknown packages that don’t do things like clobber your environment with global variables. But there’s no reason we can’t use classes in those packages that follow a standard. For all the sycophants eating the “classes are bad” dog food, you don’t need to inherit. Just like in React, where most people dont inherit. They most importantly provide a standard container for your code, and context (this). So let’s call them “containers” so the conversation doesn’t stray. We need a standard form of “container” that all Meteor activity happens within. We need a standard way most of our code looks. And this will do so much for when we’re looking at other developers’ package code or their applications. And it will do even more for new developers.

I do see a GraphQL/Relay future for Meteor, and I don’t think it depends on us waiting for subscriptions to be fully integrated into GraphQL. A lot of people seem to think that GraphQL/Relay is a fix-all for subscriptions, but the fact of the matter is that it doesn’t currently offer that. That’s not what was open sourced in the past year. They’re working on it, and it seems very close:

Here’s some extremely important links Arunoda pointed out:
https://gist.github.com/OlegIlyenko/a5a9ab1b000ba0b5b1ad (check out these diagrams + specification!)
https://github.com/graphql/graphql-js/pull/189 (a lot of implementation talk)

in his even more informative article (thanks for this):
https://kadira.io/blog/graphql/subscriptions-in-graphql

This specification isn’t based on a LiveQuery backend. It could inter-opt with it though, and there are talks regarding an official implementation (again, that would be a protocol implementation, i.e. just an official way LiveQuery adapters should be built to integrate with the greater GraphQL subscription specification).

So Meteor already offers our LiveQuery-based way of accomplishing what GraphQL subscriptions will eventually provide. We don’t need much of what GraphQL has to offer. We could even solve the oplog tailing writes issue without GraphQL. GraphQL will only become useful for us at this level once the ecosystem offers tools to help us. Which it will, which is why we should build according to their specification as much as possible. My point however (and final one) is that we don’t need to implement GraphQL completely to offer an interface to application developers. We could do what I showcased above and allow you to use graphQL to define your Mongo ORM subscriptions, and that would both "FUTURE-PROOF" us and allow our components to live in other systems that make full use of GraphQL, or perhaps systems that do the same thing and just offer client-side interoperability with GraphQL within our components.

GraphQL within our components can come as soon as we have a relational Mongo-ORM.
hell, it could exist today; the subset of GraphQL it would allow for just wouldn’t do too much without relations, which last time I checked, none of Meteor’s relational packages completely solved.


#15

Honestly, the reason why I am investing time in React, is because I simply cannot trust MDG announcements and I am not a big fan how they are orchestrating things. Whatever they do with Blaze, the party crashed. Usability has lost the battle against popularity.

So React is a fail-safe to go to another platform if necessary. JSX - not a big fan, however ES6 practice is not a bad idea either. Still consider Meteor as a good tool for the time being.


#16

@SkinnyGeek1010 thank you so much for the insightful reply.

a basic working version of their custom-insert-form.

This is also more or less what I have in mind. I’ll actually let the “html” take care of the layout. So I dropped my declarative layout requirement.

The only two things I have not yet worked out are:

  • Reactive validation (avoiding complete component rerenders)
  • Array fields where user can add/remove “rows” (and syncing validation to the correct row’s inputs)

But I guess I can take cue from autoform’s internals.

The key is making it de-coupled and generic so that you can use it in any project

Yet, these two points also again create a component-to-ss coupling. So I’m thinking, if I really want to stick with SS (mdg:method is kind of the culprit here) it still feels as if a wrapper that maps SS to a more javascript-standard validation library might be a better decoupling mechanism. What do you think?

There also are alternatives such as jumping aboard existing effors like @ffxsam’s package as a contributor.

Thank you again.


#17

Reactive validation is pretty simple, you can just pass the schema into each input as a prop and then the input can take that schema and on blur/change, validate just one key of the schema (I assume SS can do this?). The input can then take the SS error list and then render an error span with said error strings. I have my own <Input /> component that also has a hidden error div so the component itself handles displaying the error.

The form still needs to do an overall error check with all the inputs to make sure the user hasn’t skipped it somehow, but that can be done on submit.

  • Array fields where user can add/remove “rows” (and syncing validation to the correct row’s inputs)

I don’t follow what this is, perhaps a SS feature?


[quote="serkandurusoy, post:16, topic:13959"] it still feels as if a wrapper that maps SS to a more javascript-standard validation library might be a better decoupling mechanism. What do you think? [/quote]

Using something on NPM would make it more decoupled from Meteor but that should be pretty easy to convert to NPM if needed. The main feature is all of the cleanup functions, otherwise something like this would help: https://www.npmjs.com/package/schema-validator

I built my own in a few hours so if you have simple needs that’s an option too (mine is similar to the last link).

Using the approach like Sam uses works very well on a small number of inputs and makes it easy to update all of them in one file. However with hundreds of inputs this can get messy. Also if you need multiple schemas for a form than it could be an issue. His approach prob. works best for most needs though.


#18

The input can then take the SS error list and then render an error span with said error strings.

Ah yes, I keep forgetting the input is itself a component and passed the props top down.

The array thing is any form pattern where the schema has a master-detail layout, like an invoice where there are the fields that apply for the whole invoice, and then there are the line items (products added to the invoice) where you want a form layout on which you can create the whole invoice in one go, adding as many lines as needed before the submit. I’ve seen other libraries employ similar patterns.

if you need multiple schemas for a form than it could be an issue.

Do you mean conditionals or arbitrarily merged schemas?


#19

For instance in my case, a housing property has it’s own application requirements and one property might make the question ‘do you have pets’ required while another may not require it, so each property application has it’s own separate schema. Def. a one off use case though :smile:


#20

Hm, I see. I tend to think of such cases within a single schema that encapsulates “sections” and conditional validation logic. But I don’t like separating those sections. SS for insance, provides means to work either way.