Episode 04: Reactivity
In this episode of the Discover Meteor Podcast: understanding Reactivity.
- 00:46: Making Your App Come Alive
- 02:30: Reactive Data Sources
- 06:38: Reactive Data Contexts
- 10:50: Reactivity & Routing
- 13:52: Recap
- Reactivity Basics: Meteor’s Magic Demystified
- Reactive Dict, Reactive Vars, and Session Variables
Making Your App Come Alive
Sacha: Today we are talking about reactivity. Reactivity, I think it’s fair to say, is one of the main selling points of Meteor and something that makes it stand out from other frameworks.
Tom: One of the key things that Meteor does is it ties the back end of the app all the way through to your rendered HTML. The way it does that is through its reactivity system.
Sacha: In a way, reactivity is like making your app alive. If you have a dog, when the dog gets hungry, it’s going to bark. It’s going to react to the change in its own internal condition. That’s what reactivity is trying to do, connect changing input to a changing output.
Tom: To take a simple technical example, perhaps you might have a switch in the user interface which flicks from on to off based on a value in the database. How would you go about implementing that switch, which I guess is analogies to the barking state of the dog?
If you didn’t have a reactive system, you would render the system once and the on or offness of the switch would depend on what was in the database at the time that you rendered the page. If we’re talking about a website, the user would see a switch, and nothing would ever happen.
Sacha: Let’s say you want a switch to change. One simple way to do this would be through some kind of polling or timeout mechanism that queries the database every – let’s say – five seconds and then updates the switch accordingly.
Another way would be to have some type of call-back that triggers whenever the value changes in the database and then spells out the steps to take to change what the user see.
Now we come to the way Meteor implements reactivity through a system called Tracker. What Tracker does is let you write your code as if there was no reactivity at all. The syntax looks basically the same as in our first example with a static switch, except that the switch does change. That’s Meteor working its magic behind the scenes.
Reactive Data Sources
Tom: How does Tracker work? How does all this magic happen? There are two concepts that you need to understand how Tracker works. That’s the concept of a reactive source and the reactive context.
Sacha: Let’s start with the first one. What is a reactive source?
Tom: It’s something that not only tells you its current value, but also has a way, a contract of telling you in the future when its data changes. In our example, it would be the dog’s hunger state. That would be a reactive data source, because it’s telling the dog when it changes.
Sacha: The other piece of the puzzle is the reactive context, which is the environment in which the change happens. In our example, if the change is the dog’s hunger or hungry state, the context would be the dog itself as an organism.
In terms of the Meteor app, this translates into a block of code. The inside of a function that will re-run whenever a reactive data source inside it changes.
Tom: Let’s talk a little bit about reactive data sources. If you’re familiar with Meteor, this should all start, hopefully, making a little more sense. The simplest reactive data source is a Session variable, which is essentially an app wide global variable, but it happens to be a reactive global variable.
When you call
Session.get() something, you’re getting their state of it reactive variable.
Sacha: Another very commonly used reactive source are Cursors. The object’s return by collection.find call. This is why the Find method returns this Cursors objects and not just an array of documents. This is what enables Meteor’s reactivity throughout its whole stack from the database to the UI.
Tom: One things that’s really important to know is that Cursors are only reactive on the client side in the browser. They are not reactive on the server.
Sacha: That’s something we’ll talk about a bit later, which is that reactivity behaves differently on the client and the server.
Tom: Another reactive data source that’s very simple is something called ReactiveVar, which, strangely enough, is a single variable which is reactive. I guess the difference between it and the session is that it can be locally balanced. It doesn’t need to be globally available.
Sacha: Besides ReactiveVar, another way to create reactive data sources is through ReactiveDict, for reactive dictionary. Instead of simply creating a single variable, you can create a whole post of variables that they’re all going to be reactive. In a way, that’s just like creating your own session global object, or local object, but with its own name.
Tom: Another reactive data source you commonly use is the ready method on the subscription handle, which is simply a reactive variable that goes from false to true and the subscription becomes ready. You’ll see that as the template.subcriptionsReady function that you can call on the template when you’re using template levels subscriptions.
Sacha: There’s a few others reactive data sources in Meteor such as Meteor.user, Meteor.userId, Meteor.loggingIn. These are all useful shortcuts to more complex cursors or variables that are all reactive.
Tom: That also gets to the point that you can easily compose reactive data sources to create new data sources simply by writing a function. You don’t have to do anything special.
If you write a single function that maybe reads to reactive data sources and returns the sum of the result, what you’ve written is the new reactive data source. You don’t have to do any wiring yourself in order to create a reactive data source.
Sacha: To sum things up, we’ve just talked about off-reactive data sources. Let’s be clear that reactivity won’t work unless these sources are called in a reactive context.
Tom: What is a reactive context? Technically speaking, a reactive context is some function that you call that takes care of the job, listening to the sources, and re-running itself whenever they change.
Sacha: The most common reactive data context inside the Meteor app are template helpers. In fact, they are pretty much the only ones that Meteor will create and provide for you out of the box.
Tom: What template helpers do and by being a reactive context, is that the way the blaze templating system works is that whenever any of the sources that you use inside a helper change. Suppose you call a Session variable and then that Session variable changes.
The helper will magically, it seems, and re-draw the relevant session of the template with the new values. That all happens completely transparently to you. You don’t have to touch anything.
Another reactive context that you might commonly see in the Meteor app is an autorun block. An autorun block is like an even simpler reactive context. All it does is re-runs itself whenever its reactive source changes.
Suppose you have something like a console log inside your autorun, you’ll see the console log run each time the reactive data sources that it calls, change.
Sacha: Autorun is a way of taking out a small block of code and saying, “Well, basically you’ll re-run this every time something inside it changes.” That can be very handy.
Tom: Why would you want to use that, Sacha?
Sacha: There’s a few reasons. For example, template levels subscriptions. Without going too much into details, if you subscribe at the template level, you’ll often want to re-run the subscription when one of its parameters changes.
For example, if you have a limit parameter that you’re using to paginate a list of posts, when that parameter change, if the parameter is a reactive source and if the subscription call using that parameter is inside a reactive context, such as a motor run, the whole other end will re-run and the subscription will be updated with the new limit value.
Tom: Typically, you would use the template onCreated callback, which only runs a single time, and then you’d put a vista autorun inside, which creates a block inside that onCreated callback, which will run each time its reactive dependencies change.
Sacha: This is an example of having an autorun block, which is reactive, inside an onCreated callback, which is not reactive. You can see how autorun lets us have this fine grain control over reactivity.
Tom: If you want to go the other way, you can user Tracker.nonreactive to wrap a block of code in such a way that it will no longer be reactive, so you can have a non-reactive session inside a reactive context.
Sacha: What happens if you wrap an autorun inside Tracker.nonreactive?
Tom: If you wrap an autorun inside a Tracker.nonreactive, you create a new reactive context inside the non-reactive context. You can certainly have shelves of bits of reactivity in non-reactivity.
Sacha: Or you’ll open a warp hole to dimension X and swallow all the universe inside it.
Tom: That can happen sometime, too.
What else do we need to know about the intricacies of Tracker? One detail that we haven’t mentioned yet, which is probably worth talking about, is the concept of a computation. Computation is intimately linked to what we call a reactive context, which isn’t a technical concept, but more of a way of thinking about it.
A computation is the thing that sets up the context for you. For instance, a computation would be passed in as the first argument to an autorun, or it will also would be returned by the autorun function.
That computation could be helpful because it has a variable on it called firstRun, which you can use to tell whether this is the first time through the autorun block, or if it’s a later recall based on reactivity.
It also has a computation.stop method on it, which you can use to just kill the whole autorun and say, “Never run again.” Which is often something you might use if you want to wait for a set of conditions to come true.
Reactivity & Routing
Sacha: We’ve talked about a bunch of different reactive sources, we’ve talked about reactive context, and we’ve also seen how you can use Tracker.autorun to create your own reactive context and what you can do with that.
There’s another very common use case for reactivity in the Meteor apps, or not, as we’ll see, and that is routing.
Tom: The two big routers that you might have heard of in Meteor are Iron Router and FlowRouter. They take quite a different approach to how reactive they want to be. To start with, let’s talk about the Iron Router.
In the Iron Router, there’s this concept called a hook, which is a function that runs perhaps for every route or maybe just for a single route, but is a reactive function. Inside a hook you may call out to the database, grab some data, and the hook will re-run every time that data changes.
This is something that can both be very useful, but can also be quite surprising. I think it’s one of the major criticisms people have of Iron router, is that the hooks run too often.
Sacha: To give a practical example, a very common use case, waiting on data from the database inside the router hook to do something with it such as making analytics call or set the title of the page. This means that the hook will run once when you first hit that route.
When you don’t yet have the data, it will run once more once the data is available. Which, like you said, can be both good and bad.
Tom: It’s useful because you do want to wait for that data to be ready, but it’s also potentially bad if you make more than one call to your analytic server and you start over counting the number of visitors you have to your site.
Sacha: Now, on the other hand, FlowRouter uses a completely different approach and it’s basically not reactive at all.
Tom: In FlowRouter, the hooks aren’t reactive. You might wonder, “How am I supposed to do things like waiting on data?” The answer there is that FlowRouter doesn’t want you to wait on data in the router. It hands the responsibility for that over to the templates stack.
Sacha: FlowRouter has a very hands-off approach compared to Iron Router. FlowRouter says, “Well, my job is routing, not anything else. Deal with it.”
Tom: [laughs] Things that are worth knowing in FlowRouter is that certain functions are reactive, such as
FlowRouter.getParams(). You might want to call out from inside a helper or in an autorun block. If it’s setting up subscriptions in a template, put
FlowRouter.current() isn’t reactive.
If you want to make it reactive, you need to call
Sacha: I think it’s interesting to compare both approaches to reactivity. On one hand, Iron Router tends to be reactive by default and you have work to make things non-reactive. FlowRouter takes the opposite approach where things are not reactive by default. If you do want reactivity, then you have to specify it.
Let’s recap. We’ve learned that reactivity is the engine that makes your Meteor app come alive, and that it’s architectured around two main concepts. Reactive data sources and reactive context. We’ve also seen how to create your own custom reactive context with Tracker.autorun, and finally how routing can sometimes be reactive or not.
If you want to go further and learn more about reactivity, we have a good write up on our blog that kind of makes a lot of the same points that we’ve just covered here. There’s also a good article on “The Meteor Chef.”
In terms of other resources, PeerLibrary has a few good packages such as a server autorun, which as its name implies, makes it possible to have autorun on the server, as well as the client. I’ll leave links to all of that in the show notes and I encourage you to take a look.
In conclusion, I think reactivity can be a very powerful tool. It’s certainly something that’s really core to Meteor and makes it stand out from other frameworks. Yet, at the same time, it can also be easy to shoot yourself in the foot with it. Things can spiral out of control sometimes. Hopefully, this episode will help you get a better understanding of the logic behind reactivity.
As always, thanks for listening.
Sacha: If you want to help support the show, please don’t hesitate to leave a review on iTunes. Every time we see a new review we’re excited and we save it to add it to our collections. This, in fact, brings me to the topic of the next episode, which will be: collections.