Are cursors really reactive data sources? and a small journey to understand the magic of {{#each}}

I was reading through The Meteor Manual when I came across this tidbit which thoroughly surprised me:

Using Collection.find() doesn’t set up any reactive dependencies.

I didn’t think I had been asleep at the wheel, but I wouldn’t be surprised if I misunderstood something. So I jumped over to the official docs and took a closer look. I think the message is a bit muddled because of the opening line below, but sure enough it is at least indirectly implied that cursors alone will not setup reactive dependencies.

From: http://docs.meteor.com/#/full/find

Cursors are a reactive data source. On the client, the first time you retrieve a cursor’s documents with fetch, map, or forEach inside a reactive computation (eg, a template or autorun), Meteor will register a dependency on the underlying data."

“But wait!” I said, "don’t they return only cursor.find() in plenty of examples that are in theory reactive?

Some more sifting around in the docs shows this to be true:

<!-- in myapp.html -->
<template name="players">
  {{#each topScorers}}
    <div>{{name}}</div>
  {{/each}}
</template>

// in myapp.js
Template.players.helpers({
  topScorers: function () {
    return Users.find({score: {$gt: 100}}, {sort: {score: -1}});
  }
});

Even though this does work, how can it? A call to Users.find() does not setup any reactive dependencies, right? Or am I taking crazy pills!

My search for answers eventually lead me to the spacebars documentation which provides what I believe is the closest thing to answering my question(s).

So I am left with the educated guess that passing a cursor to the {{#each}} helper, or for that matter the {{#if}} or {{#with}}, leads eventually to the calling of .fetch(), .map(), or .forEach() on the cursor. This in turn establishes reactive dependencies.

If my assumption is correct then I guess this story is just a vote to make this more explicit in the documentation. Additionally maybe there is a nuanced differentiation between a “Reactive data source” which the documentation calls all cursors, and something that establishes reactive dependencies inside an autorun or template (or maybe not).

Also does passing a cursor vs. the results of fetch() to {{#each}} still provide the same benefit described here now that Blaze is used? In the latter case would the whole {{#each}} block be re-rendered if the data in the cursor changes?

1 Like

After reading the Discover Meteor ebook. It’s the subscribe() that is reactive and send a push/update whenever the collections have received new data after the current index in cursor, therefore, rerender the template automatically without having to reload the page.

Correct me if I’m wrong.

When you call Collection.find() (no matter fetch()) you are set up new observer with the Cursor returning by Collection.find(). So Cursor is a simple IdMap instance that contains all the documents, relevant to Collection.find() selector and options. Every Cursor has its own observer registered for the Collection. It means that Cursor doesnt have a dependency but while it lives all the Collection’s changes affects on a Cursor’s IdMap and re-runs Blaze Each helper.

cursors are reactive data sources, but they can’t force recurring computations on their own

when you use a reactive data source inside a helper, Blaze automatically uses this dependency and re-runs the helper everytime the source changes

however, if I do something like

Template.foo.created = function() {

    Collection.find();

}

the created event wouldn’t be automatically re-computed, unless I wrap the cursor with a Tracker/Template.autorun or write up a dependency relationship manually (using Tracker.Dependency)

so perhaps the documentation requires some revising for better clarity

as for Blaze’s built-in tags, you don’t need to explicitly call fetch() - Blaze will do that implicitly with cursors
this is very convenient when you want to directly use cursors stored in the template’s data context, without helpers

A cursor passed to an #each block helper is passed to a package called observe-sequence that starts an .observe() on the cursor.

1 Like

I would say that cursors are a reactive data source in the sense that attempting to get data from a cursor through any provided method (i.e. fetch, map, forEach and not the underlying map) in the context of a reactive computation, will result in dependency registration.

Exploring Further

forEach is actually the originator of dependency registration and fetch and map call forEach internally.

LocalCollection.Cursor.prototype.forEach = function (callback, thisArg) {
  var self = this;

  var objects = self._getRawObjects({ordered: true});

  if (self.reactive) {
    self._depend({
      addedBefore: true,
      removed: true,
      changed: true,
      movedBefore: true});
  }

  _.each(objects, function (elt, i) {
    // This doubles as a clone operation.
    elt = self._projectionFn(elt);

    if (self._transform)
      elt = self._transform(elt);
    callback.call(thisArg, elt, i, self);
  });
};

Taking note of the self._depend

LocalCollection.Cursor.prototype._depend = function (changers, _allow_unordered) {
  var self = this;

  if (Tracker.active) {
    var v = new Tracker.Dependency;
    v.depend();
    var notifyChange = _.bind(v.changed, v);

    var options = {
      _suppress_initial: true,
      _allow_unordered: _allow_unordered
    };
    _.each(['added', 'changed', 'removed', 'addedBefore', 'movedBefore'],
           function (fnName) {
             if (changers[fnName])
               options[fnName] = notifyChange;
           });

    // observeChanges will stop() when this computation is invalidated
    self.observeChanges(options);
  }
};

So my opinion is that while we don’t get a reactive computation by just calling a find, its still a reactive data source because calling collection.find() alone would not be useful and so you’ll always find yourself with the result of a find, fetch or forEach.