• Meteor

Reactive Subscriptions

From the class:  Tracker

Let's take a look at what happens when you call the Subscribe function of Meteor inside of a reactive computation. So here's an example of calling the Subscribe function inside of a computation that's created with the Auto Run function. And then using a reactive variable-- in this case, session-- to determine which subscription to go with, so which parameter to pass up. In this case, it's passing the chat room that we want to subscribe to.

To make this example easier to see, I've moved some of the previous code into the Lib directory so you can find the old reactive data source here and the SayHello function here. And in our Application.JS file, I've added a server block of code, and I've also added a new Mongo collection called Items.

And in the server block, I created a Publish function where we will publish out an item with a particular title. And that title is going to be passed up as a parameter. Down here, we're going to see the database with 10 items if they don't already exist-- so the titles would be Item 0, 1, 2, and so on.

And what I want to do is to fill out Is Client block, creating a reactive subscription call. Now, in order to do this, we need to first go and remove the Auto Publish package that's installed by default. I'll type media remove auto publish and then confirm that it's gone by using the media list command. And I should see that the Auto Publish package is gone at this point.

Now we need to go back and fill in the client part of the code. So I'm going to subscribe to the item publication, and we're going to pass up a title as a parameter-- for example, Item 0 or item 1 and 2. But I want to grab this value from a reactive data source, and so we'll just use session this time instead of creating a custom reactive data source. So I'll call the Get function, and we're going to grab the value of the title.

Let's set a default value for this key. So I'll say Set Default. And this way, when we first subscribe, we'll have an actual item. Now, I haven't created a computation yet. Notice there's no Auto Run Function call here. And Meteor is not going to create a computation for us automatically. So we need to do that.

So I'm going to say track our Auto Run and call that function with a callback. And now the Subscribe call is running inside of a computation, and it's reactive. So here's our subscription message. It's selected here where we're subscribing to the item publication passing up the title. And if you look down a little bit, you'll see that the added message gets sent down. And so this is the actual item being published down to the client.

And you can verify one last time by in the console saying Items Find Fetch and making sure that the item is indeed in that list. Now, what I want to do is to change the reactive variable. So I'll say session.set, and I want to change the title to something different. So let's call it Item 1. And take a look at what happens here.

Because the subscription call was made inside of a computation, that computation was re-run, and it now subscribes to the new subscription with the new parameter of Item 1 when previously it was Item 0. And notice here that the first thing that Meteor does is unsubscribe from the previous subscription. So it automatically tears down the previous subscription.

And as a part of stopping the previous subscription, it removes any of the old items that are now no longer relevant. So moving a little bit faster, then I can draw. But then it adds the new item. So it's going to remove the previous item, because this is technically a new subscription. And if we can come down here and do a query, we can verify that. So I'll say Items Find Fetch.

And notice that there aren't two items in here. There's just one with a new item that we requested, Item 1. So Meteor is automatically tearing down the previous subscription or stopping the previous subscription when the value of the parameter changes as long as it's inside of a computation.

Now, what happens if we re-run this computation and the value of Title hasn't changed? So an example of this might be where you're navigating from one page to another, and you're using the routing package, and the actual value hasn't changed for a subscription. Will Meteor actually tear down the subscription anyway and redo everything unnecessarily? And it turns out it will not. It's pretty smart about figuring out that a subscription is the same subscription and not tearing it down unnecessarily.

Let's just make sure that that's true. So I'm going to assign the result of Tracker Auto Run to a computation, and I'm just going to invalidate it manually to make sure that if we re-run it over and over again but the value of the title hasn't changed, that it's not to doing extra work.

I'll call the Invalidate function of the computation. And what I'm trying to get it to do is to re-subscribe. But it won't, because the value of the parameter has not changed, and so it's smart enough to know that the subscription is the same as what it was previously. So notice as I call the Invalidate function, even though that function is re-run, that no messages are sent up the wire. So the subscription does not re-run.

To see how this is implemented so as you can study it a little bit more on your own, I've taken us over to the Meteor source code. And I'm inside the Packages folder looking at the DDP Client Package. And I've opened up the Live Data Connection file, and we're looking at the Subscribe function. So this is the actual source code for calling subscribe or for the Subscribe function in meteor.

And if you scroll all the way down to the bottom of this function-- so all the way down-- you'll see some code that should look familiar are now. So first, it's checking to see, are we running this Subscribe call inside of an active computation? Is Tracker active? And if it is, then we'll pass in a callback to On Invalidate. So when we invalidate this computation, go ahead and store away the subscription and mark it as inactive.

And then once the flush has completed, if this subscription is still inactive, then call the Stop function on it. Otherwise, don't do anything. Just leave it alone.

So an important implication of this implementation is that it only works from one flush to the next. So for example, if you're routing around your application and you move from one page to the next, if the subscriptions haven't changed, then they won't be torn down. But if you go to three or four pages away and the subscription is different, then it won't matter. So this works from one flush to the next flush. So hopefully, this gives you some insight as to how subscriptions work inside of a computation.

I'd like to show you a useful technique for determining where a computation or why a computation was invalidated. I've updated our app code a bit and added a helper to a template called Last Item, and I'm rendering that out here. And what I want to do is to just print out the title of the last item in the list. So in this helper here in Item, we'll return the actual item data context. And here, I'm subscribing and publishing out all of the items in the list.

So to do this, I'm going to make use of a reactive data source. And to start off, I"ll get a cursor by calling items.find. Now, I want to make sure that it's clear that the Find function itself is not reactive. So Find just returns an object that is called a cursor. And that object has functions on it like Fetch and Map and For Each. And it's these methods that are the reactive functions.

So what we want to do is to call the fetch function here and then just pop off the last item. And we'll just call that our last item. So I'm going to keep this variable assigned to cursor so that it's clear. And the item itself, we'll get an array, and then pop off the last item in that list, and then just return the item. Now, let's make sure this is working before we go too far.