This handle.ready() code is ugly - how to make it pretty?

ListingForm = React.createClass({
  mixins: [ReactMeteorData],
  getMeteorData() {
    var data = {};
    var categoriesHandle = Meteor.subscribe('categories.all');
    var citiesHandle = Meteor.subscribe('cities.all');
    if (categoriesHandle.ready() && citiesHandle.ready()) { // YUCK
      data.categories = Categories.find().fetch();
      data.cities = Cities.find().fetch();
    }
    return data;
  }
});

I’m subscribing to two collections in my component (they populate two different select elements) and the code for the handle ready checks looks bad. What if I subscribe to five different collections in one component down the line, would I need to do?

if (a.ready() && b.ready() && c.ready() && d.ready() && e.ready()) { }

Is there a better way to handle this? :cold_sweat:

1 Like

With https://github.com/kadirahq/subs-manager, I believe that you can do:

const subs = new SubsManager();

subs.subscribe('workspaces');
subs.subscribe('user.single', 123);

if (subs.ready()) {
  // lift-off
}
3 Likes

That looks nice, does it work well with React and the way it mounts/dismounts components?

Im actually going to implement something like this. Much like template.subscribe giving you Template.subscriptionsReady Essentially look at that code and it can guide you to a solution

You won’t, if you want to stay sane. :slightly_smiling:

I personally do this:

ListingForm = React.createClass({
  mixins: [ReactMeteorData],

  getMeteorData() {
    let categoriesHandle = Meteor.subscribe('categories.all');
    let citiesHandle = Meteor.subscribe('cities.all');

    return {
      ready: categoriesHandle.ready() && citiesHandle.ready(),
      categories: Categories.find().fetch(),
      cities: Cities.find().fetch()
    }
  }
});

It’s a little neater, I think. Or use the subs-manager package mentioned above, in addition to the structure above.

2 Likes

Another option to consider - return an array of Collection.Cursor's from your Meteor.publish:

Meteor.publish('categoriesAndCities', function () {
  ...
  return [
    Categories.find(),
    Cities.find()
  ];
}

And then:

getMeteorData() {
  var categoriesAndCitiesHandle = Meteor.subscribe('categoriesAndCities');
  if (categoriesAndCitiesHandle.ready()) {
    data.categories = Categories.find().fetch();
    data.cities = Cities.find().fetch();
  }
  return data;
}
3 Likes

I like this one the best. Pretty good.

I can see a large form with multiple select elements populated by multiple mongo collections, not really something crazy or a hypothetical, just based on previous business needs I had.

I agree, but I’d still manage it with methods instead of publications.

You can put all the handles in an array and use the every method:

[a, b, c, d, e].every(function(h){ return h.ready() })
5 Likes

Noice - that seems like an even better approach.

This is what I do too, though it can be even more terse with an es6 arrow function:

[a, b, c, d, e].every(h => h.ready());
4 Likes

Noice-er! Now now, this ain’t code golf guys. :slight_smile:

Didn’t know about every! Learn something new every day. :slightly_smiling:

2 Likes

A large form with multiple select elements seems like it would be best split into a component for each select. Then you only need one subscription per component.

1 Like

True true, but then I do enjoy having a dumb <SelectInput options={} value={} component and subscribe in the containing element.

Unless, I actually have a container within the form container for the select data fetching, in which case how far does this rabbit hole go. :stuck_out_tongue:

CreateProduct (container) -> ProductForm (dumb) -> CategoriesSelectWrapper (container) -> SelectInput (dumb)

1 Like