Handling MongoDB reactive data in Vue application with vue-meteor-tracker

I have a bootstrapped Meteor 3 project (meteor create --vue my-app --release=3.0.1), using Vue 3 (written in composition API style) along with vue-meteor-tracker package (GitHub - meteor-vue/vue-meteor-tracker: Use Meteor Tracker reactivity inside Vue components). My application is currently structured as described in Meteor guide (Application Structure | Meteor Guide), specifically, my ui directory looks like this:

 -/imports/ui
  |-App.vue  // app layout with header, footer and content handled with <RouterView />
  |-main.js  // app entrypoint
  |-router.js  // routes definitions
  |
  |-components  // simple components (e.g. layout elements or other DOM fragments)
  | |-Footer.vue
  | |-Header.vue
  | |-...
  |
  |-views  // pages/views (used in router.js routes definition) 
  | |-HomeView.vue
  | |-OtherView.vue
  | |-...

I’m struggling to understand how exactly to subscribe and track MongoDB data with vue-meteor-tracker to have it available in all my views (pages) or components.

If I understood the logic behind subscribing to and tracking the data, it works like this:

  1. if I subscribe to a collection in HomeView.vue, I won’t be able to track this data in OtherView.vue (unless I re-subscribe there again)
  2. if I subscribe to a collection in App.vue, I’ll be able to track the data in all my views and components
  3. if I subscribe to a collection in App.vue, I’ll be able to track the data in all my views and components in different ways if I need to (e.g. use one MongoDB query in HomeView.vue and a different query in OtherView.vue)

I’d really like to know if these “assumptions” are correct to begin with?

Now, let’s say I have a specific query which I’d like to use in multiple views or components. To avoid writing the same query multiple times in my views/components, I did the following:

  1. I subscribed to my collection in App.vue
  2. I created a new file /imports/ui/trackers.js
  3. in trackers.js I wrote my query/queries and exported them:
import { autorun } from 'vue-meteor-tracker';
import { MyCollection } from '../api/MyCollection';

export const myCollection = autorun(() => MyCollection.find().fetch()).result;
  1. wherever I need this data, I simply import the variable from trackers.js:
// e.g. `OtherView.vue`
<script setup>
import { myCollection } from '../trackers';
</script>

<template>
  <div>Collection size: {{ myCollection.length }}</div>
  <div v-for="item in myCollection">{{ item.name }}</div>
</template>

As far as I can tell, this works fine, I can see myCollection data as expected.

However, with this approach, I’m getting a warning in browser’s console:

[trackers.js] 'autorun()' should only be used in setup() inside components to clean up correctly. If you need to call 'autorun' later outside of the setup context, use 'useAutorun()' instead.

If I change my code to use useAutorun(), as suggested in the warning message, and change the code in trackers.js to this:

import { useAutorun } from 'vue-meteor-tracker';
import { MyCollection } from '../api/MyCollection';

const { autorun } = useAutorun();

export const myCollection = autorun(() => MyCollection.find().fetch()).result;

I can still use my data “as expected” and everything seems to work fine again. But now I see a different warning message in browser’s console:

[trackers.js] [Vue warn]: onUnmounted is called when there is no active component instance to be associated with. Lifecycle injection APIs can only be used during execution of setup(). If you are using async setup(), make sure to register lifecycle hooks before the first await statement.

Given that I see these warnings whichever method I use to track my data with vue-meteor-tracker, I think I’m doing something wrong here.

My questions are actually very simple – am I understanding subscription/tracking logic correctly and what would be “the correct” way to structure my app to be able to write queries which I could use in any view/component?

Thank you very much in advance!

I’m in the process of migrating a meteor2/vue 2 app to meteor 3/vue 3. To get started, I looked at the following resources:

  1. meteor 3 vue skeleton with the meteor create command
  2. meteor-vite/examples/vue at main · Akryum/meteor-vite · GitHub
  3. meteor-vite/examples/meteor-v3-vue at release · JorgenVatle/meteor-vite · GitHub

From number 2 and 3 above you can see that vue-meteor-tracker is not used any more in projects that use meteor-vite. I think vue-meteor-tracker has been left in meteor’s own vue 3 skeleton by mistake.

What works for me is this:

Just importing tracker and running meteor subs and queries inside it.

The example 2 above also includes an example of using tracker with the options API, but that has not worked for me. Maybe it’s legacy code. meteor-vite/examples/vue/imports/ui/LinksOption.vue at main · Akryum/meteor-vite · GitHub

Thanks for the answer! I was asking myself the same thing about vue-meteor-tracker after seeing some of the examples around which are not using it at all, but I followed the latest (preview) tutorial for starting with Meteor 3 and Vue application just yesterday (I believe it will be published very soon), and vue-meteor-tracker is used there indeed, so I’d say it’s not abandoned or anything like that and that was my starting point where I expanded my app from.
After all, I might just use Tracker in my app, I think it should be fine for my use case, but given what I’ve said about the official tutorial, I think think kind of information would be useful not only to me, but for general use-case if someone decides to keep using vue-meteor-tracker.

By the way, forgot one link. This is a vue 3 example repo by a meteor team member:

Hopefully soon enough the meteor vue 3 skeketon will be updated with correct code. Until then I’d recommend just doing what you see in Jorgen Vatle’s repo or Henrique’s repo above, they’re both doing the same thing basically.

My plan has been to finish my migration and then look around a bit to see if any PRs are needed. But that will take a while.

Yea I get your point and thanks for sharing all this and helping around, but as I mentioned, I was following a soon-to-be-official Meteor/Vue tutorial shared by Frederico (CEO) yesterday on Slack where he’s using vue-meteor-tracker included in the skeleton. Not sure if I should link the tutorial here since it’s still a preview, but you can find it on Slack easily (I can tag you in relevant thread if you want). I’d really prefer to handle this logic based on that tutorial, rather than some other approach, but I’ll definitely take a look at the examples you shared and will try to handle what I need by using just Tracker instead of any other package wrapping it.

1 Like

Hey @_errata! It’s fine to share the links. There’s an open PR, and here’s a preview of the tutorial. No one from the team could review it yet, so I appreciate any feedback.

I discussed this with the team and left these comments on the PR:

  • I aimed to keep it similar to the Meteor 2.0 and Vue 3 tutorial, though there is room for improvement.
  • The vue-meteor-tracker package is in beta, lacks async support, and isn’t maintained. I suggest we remove it from the vue skeleton. Then, we can update this tutorial and replace it with Meteor core packages.

So, the plan is to take the package out of the Vue skeleton and follow the examples @vooteles mentioned. It’s still included in the new tutorial because I think it would be confusing to generate the project and then remove the files and packages, making it totally different from the skeleton.

What do you think? Maybe we should show the best approach in the tutorial, even if it’s different from the skeleton. And in Meteor 3.0.3, we can work on the Vue skeleton to align it properly.

1 Like

Thanks for your comment and clarifications @fredmaiaarantes, extremely appreciated! :heart:

Good to know about the team’s plan of removing vue-meteor-tracker so that I (or anyone else) don’t focus on it anymore.

It’s still included in the new tutorial because I think it would be confusing to generate the project and then remove the files and packages, making it totally different from the skeleton.

I agree with this, although what you mentioned later

Maybe we should show the best approach in the tutorial, even if it’s different from the skeleton. And in Meteor 3.0.3, we can work on the Vue skeleton to align it properly.

would be also a good way to go IMHO. Just a simple instruction to remove this one package and a note that “this step won’t be required soon™” would be already good I think, some additional word or two of reasoning would be even better, of course.

However, I can’t tell if you guys will really have time and resources to deliver this in Meteor 3.0.3? I would even offer my assistance, but somehow I think I’m miles away to contribute to core code, but who knows, maybe there is something I can do, at least some simple annoying chores :sweat_smile: Anyways, if you think it will be delivered in 3.0.3, I would rather see a tutorial without using deprecated package than providing information to people (especially newcomers) which will be outdated very soon. Those are just my 2 cents.

On a side note, what I personally would also like to see in this tutorial is a simple example of how to handle MongoDB data across the whole application, the “problem” I actually started this thread about :slight_smile: I think this is not an unusual scenario and would definitely help to kick off your new Meteor/Vue project even faster.

Thanks again and really looking forward to all the new tutorials!!

1 Like

Well, today I reached a point in my app migration to Meteor 3 and Vue 3 where I had to deal with a first subscription that has dynamic parameters (for example a subscription for a date that can change at any time based on user selection) and I realized that I might have been too hasty in my comment above about vue-meteor-tracker.

The parameter is a Vue reactive data source (ref). And when that changes, I need to update the subscription to reflect the new data. How do you guys achieve that? vue-meteor-tracker makes it pretty easy as you can see from examples here:

However, could I not just replicate this functionality with a Vue watcher? Watch the parameter variable for a change and when an update occurs, call a subscription with the new value. Or am I missing some functionality here?