svelte-meteor-data icon indicating copy to clipboard operation
svelte-meteor-data copied to clipboard

A reactive subscription creates infinite amount of subscriptions when props change

Open arggh opened this issue 5 years ago • 3 comments

When I create a subscription that reacts to changing props like so:

<script>
export let page;
$: Meteor.subscribe('stuff', { page });
</script>

...the old subscriptions will not be stopped until the component is destroyed. That means, if page changes a lot, a lot of "stale" subscriptions will be persisted until the component is destroyed.

Screenshot 2020-07-24 at 15 28 24

Wouldn't it make sense to close the old subscription when the props change?

arggh avatar Jul 24 '20 12:07 arggh

Yes, this is a known shortcoming. I have not found a clean way to address this yet. You'd need to do something like this:

let subscription;

$: {
  if (subscription) subscription.stop();
  subscription = Meteor.subscribe('stuff', { page });
}

The way to make this work out of the box would require hooking into the Svelte update call such that it stops all the current subscriptions on every update. However, the problem with that is that not every $: block is rerun on an update (only the ones for which dependencies changed), so we'd need to be selective about which subscriptions to stop, and we just don't have that information. The only way this would work is if we forced Svelte to re-run all of the $: lines on every single update, which would basically cripple performance.

So, I don't think this can be cleanly fixed without patching the Svelte compiler itself to create some kind of guard block that would let us do this.

The only other way I can think of addressing this would be to discourage Meteor.subscribe and instead create a separate store-based interface that would work something like this:

$: sub = subscribable('stuff', { page });
$: $sub

The syntax is a little weird and not intuitive though, with the way that the store needs to be explicitly instantiated like that.

Other thoughts are welcome.

rdb avatar Jul 24 '20 12:07 rdb

Oh, I haven't tried it, but this actually might work today, without a specific subscribable store:

$: sub = useTracker(() => Meteor.subscribe('stuff', { page }));
$: $sub

rdb avatar Jul 24 '20 12:07 rdb

Both of these suggestions seem to work, thank you! Had some trouble at first due to a forgotten {#await Meteor.subsribe(...} below the <script> block...

I prefer this approach

let subscription;

$: {
  if (subscription) subscription.stop();
  subscription = Meteor.subscribe('stuff', { page });
}

since it's obvious what's going on, the last example is...not.

Still, would be great to find a way to make it work out of the box! Have you tried asking for comments in the Svelte Discord channel?

arggh avatar Jul 24 '20 14:07 arggh