Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In plain English, what does Tracker.autorun do?

The Tracker methods don't exactly belong to the core of Meteor's functionality, are seldomly used in tutorials and beginner books (and if they are they are not explained very well), and as a consequence are considered a lot more "scary" than most of the rest of the framework.

I, for one, have never managed to wrangle Tracker.autorun into a project of mine as it never seems to do what is expected of it. This is what the docs say it does:

Run a function now and rerun it later whenever its dependencies change.

To me this sounds like a way of making non-reactionary sources reactionary, but then you come to the examples, the first of which looks like this:

Tracker.autorun(function () {   var oldest = _.max(Monkeys.find().fetch(), function (monkey) {     return monkey.age;   });   if (oldest)     Session.set("oldest", oldest.name); }); 

How exactly is this different from not using Tracker.autorun? Cursors are already a reactionary source, and to make matters more confusing the next example deals with another reactionary source: Sessions.

Does Tracker.autorun only work with reactionary sources, and if so what is the benefit of using them inside a Tracker? Making them doubly reactionary?

like image 690
Yeats Avatar asked May 30 '15 12:05

Yeats


1 Answers

To implement reactive programming (a variant of event driven programming), Meteor uses 2 different concepts :

  • reactive computations : pieces of code that will reactively re-run each time their underlying dependencies are modified.
  • reactive data sources : objects capable of registering dependencies when used inside a reactive computation, to invalidate it and make it run again with the new data value.

These two concepts are implemented by two rarely used underlying Tracker objects, namely Tracker.Computation and the helper object Tracker.Dependency which is a container for storing a set of computations.

A Tracker.Computation is an object with 2 important methods :

  • invalidate(), which is causing the computation to rerun.
  • onInvalidate(callback) for actually running the computation arbitrary code.

When you call Tracker.autorun, you're basically creating a new computation and registering an onInvalidate callback with the function you pass as argument.

A Tracker.Dependency is a collection of computations with 2 methods.

  • depend() : adds the current computation to the set.
  • changed() : when called, invalidates every registered computations.

When a reactive data source registers a dependency inside a computation, it is calling Dependency.depend(), which simply adds the current computation (if any) to the set of tracked computations.

When the reactive data source is modified, it is calling Dependency.changed() which is going to invalidate every registered computations in the set.

Source : The Meteor Tracker manual.

In the Meteor framework, you usually only deal with several higher level objects implementing the concepts of reactive programming.

  • reactive computations are spawned using Tracker.autorun, by default template helpers are always run inside a reactive computation.
  • reactive data sources are using Tracker.Dependency to invalidate computations, they include MiniMongo cursors, Session variables, Meteor.user(), etc...

Use Tracker.autorun when you need to reactively rerun arbitrary code outside of template helpers, for example inside a template onRendered lifecycle event, use the shortcut this.autorun (spawning a reactive computation that is automatically stopped when the template is destroyed) to react to any reactive data sources modifications.

Here is a small example of a template that counts how many times you clicked a button and reset the counter to 0 when clicked 10 times.

HTML

<template name="counter">   <div class="counter>     <button type="button">Click me !</button>     <p>You clicked the button {{count}} times.</p>   </div> </template> 

JS

Template.counter.onCreated(function(){   this.count = new ReactiveVar(0); });  Template.counter.onRendered(function(){   this.autorun(function(){     var count = this.count.get();     if(count == 10){       this.count.set(0);     }   }.bind(this)); });  Template.counter.helpers({   count: function(){     return Template.instance().count.get();   } });  Template.counter.events({   "click button": function(event, template){     var count = template.count.get();     template.count.set(count + 1);   } }); 
like image 114
saimeunt Avatar answered Sep 22 '22 17:09

saimeunt