meteor-vue / meteor-vue3

MIT License
19 stars 8 forks source link

autoSubscribe: only stop subscriptions if the arguments change #11

Open nathan-muir opened 2 years ago

nathan-muir commented 2 years ago

Given a simple usage of autoSubscribe such as:

<script setup>
import { useRoute } from 'vue-router'
import { autoSubscribe } from 'meteor/vuejs:vue3'
const route = useRoute();

autoSubscribe(() => ['publicationName', route.params.id])
</script>

Currently, each time the route changes/invalidates, even if the route.params.id does not change, it will send an "unsub" and "sub" message to the meteor server. Causing the publication to be re-run on the server using additional (unnecessary) resources.

The solution is to copy the pattern from the built-in meteor/ddp-client subscribe function, where it integrates with Tracker.

Here is the relevant code:

class DDPClient {

  subscribe(...) {
      /* ... */
    if (Tracker.active) {
      // We're in a reactive computation, so we'd like to unsubscribe when the
      // computation is invalidated... but not if the rerun just re-subscribes
      // to the same subscription!  When a rerun happens, we use onInvalidate
      // as a change to mark the subscription "inactive" so that it can
      // be reused from the rerun.  If it isn't reused, it's killed from
      // an afterFlush.
      Tracker.onInvalidate((c) => {
        if (hasOwn.call(self._subscriptions, id)) {
          self._subscriptions[id].inactive = true;
        }

        Tracker.afterFlush(() => {
          if (hasOwn.call(self._subscriptions, id) &&
              self._subscriptions[id].inactive) {
            handle.stop();
          }
        });
      });
    }
  }
}

This pull request attempts to approximate Tracker.afterFlush by using Promise.resolve(); I'm not aware if there's a better method for vue reactivity.