Can I avoid to stop subscriptions when a reactive argument changes?


#1

I am using React and withTracker to run a reactive subscription. The subscription accepts one reactive arguments, so when it changes, subscription is run again. It works fine, but I am wondering if I can optimize it.

Everytime my reactive argument changes, withTracker runs again, so publication is run again too. I’ve notice that when publication is run again, it actually stops and create a new one. In the new publication, the mongoDB query is entirely run again, even if DDP just send me new records then.

Can I avoid the subscription to stop everytime my reactive argument changes?

This is the code I used to test, it is a simple blog home page that displays last articles with an infinite-scroll system:

Container:

import { Meteor } from 'meteor/meteor';
import { withTracker } from 'meteor/react-meteor-data';
import { ReactiveVar } from 'meteor/reactive-var';

import PublishedArticles from '../../api/published-articles/published-articles';
import HomePage from '../components/HomePage';

const limit = new ReactiveVar(5);

const HomeContainer = withTracker(() => {
  Meteor.subscribe('publishedArticles.infinite', { limit: limit.get() });
  const seeMore = () => {
    limit.set(limit.get() + 1);
  };
  return { seeMore, articles: PublishedArticles.find().fetch() };
})(HomePage);

export default HomeContainer;

Publication (low level API for debugging purposes):

import { Meteor } from 'meteor/meteor';
import PublishedArticles from '../../published-articles';

Meteor.publish('publishedArticles.infinite', function({ limit }) {
  let count = 0;
  const handle = PublishedArticles.find(
    {},
    { fields: { _id: 1, title: 1, content: 1 }, limit },
  ).observeChanges({
    added: (id, fields) => {
      count += 1;
      console.log(count);
      this.added('publishedArticles', id, fields);
    },
  });
  this.ready();

  this.onStop(() => handle.stop());
});


#2

There isn’t really any way to stop this with your current architecture. What you’ll want to do is rewrite things so that your subscription asks for a page, use limit and skip in your publication to return a number of records for each subscription and then use a loop to subscribe to each page. Any time that there is a rerun the loop will run, but the old subscriptions will be reused and not stopped. Only the new subscription will be run and provide the additional data that you need.


#3

Also don’t forget to check() your publication arguments.


#4

Sure it is just to simplify code here.

So I see what you mean. Actually I am now using a method because I don’t need reactivity. I don’t want the layout to move if a new blog post is created. I first built a method using limit and skip. Then I realized skip could be slow with a lot a records, so I am using a start date and a limit as recommanded in MongoDB doc in the skip page.

Just for curiosity, I don’t really see why using a limit and skip won’t stop publication, but just a limit does. Could you explain this?


#5

Any subscribe call that uses the exact same params will be reused. The first time your reactive var is 1 so the loop calls subscribe once with the page param set to 1. Then you increment the reactiveVar causing the withTracker function to rerun. The loop then calls the subscribe twice, once with the page param set to 1 which reuses the old subscription, and once with the page param set to 2 which will create a new subscription and send the new data. Since the first subscription is reused, it won’t be stopped.