How to update ReactiveVar

Hello guys … I am getting data from a server method and setting a reactive var in the template onCreated method

Template.notificationdropdown.onCreated(function() {
    const instance = Template.instance();
    instance.notificationsData = new ReactiveVar();

    instance.autorun(() => {
        Meteor.call('fetchNotifications', Meteor.userId(), (err, res) => {
            console.log('NOTIFICATION RES', res);
            instance.notificationsData.set(res);
        });
    });
});

I need to update that notificationsData reactiveVar as soon as there is a activity in the Notification collection. At the moment if there is a new document added in the collection I have to refresh the page in order to see that new data. What approach should i use to make this instantaneous. Thanks in advance.

It looks like you should be using a publication instead of a meteor method. Meteor methods aren’t reactive, so you’re only fetching the notifications once.

What is fetchNotifications doing?

Agree with cereal. If this is truly a collection then you should just subscribe to it. Inside of an autorun you would do your find and it would be re-run any time the collection changes. If instead your notifications are not actually a collection, you would need to do the meteor call at some polling interval. The way you have your code, it would only call it once (well technically it would call it again if the Meteor.userId() ever changed because I believe that is reactive).

fetchNotifications(userId) {
        const notificationsArr = [];
        const notifications = Notification.find({ userId: userId }).fetch();
        for (const notificationId in notifications) {
            const jobData = JobListings.findOne({ _id: notifications[notificationId].jobId });
            const discussionData = ResumeDiscussion.findOne({ jobListingId: notifications[notificationId].jobId });
            const matchData = Matchs.findOne({ _id: notifications[notificationId].matchId });
            let notificationData = {};
            notificationData.id = notifications[notificationId]._id;
            notificationData.event = notifications[notificationId].event;
            notificationData.userId = notifications[notificationId].userId;
            notificationData.jobId = notifications[notificationId].jobId;
            notificationData.seen = notifications[notificationId].seen;
            notificationData.isDeleted = notifications[notificationId].isDeleted;
            notificationData.type = notifications[notificationId].type;
            notificationData.createdAt = notifications[notificationId].createdAt;
            notificationData.jobName = jobData.title;
            notificationData.jobId = notifications[notificationId].jobId;
            notificationData.matchId = notifications[notificationId].matchId;
            notificationData.matchData = matchData;

            notificationsArr.push(notificationData);
        }
        return notificationsArr;
    },

This code would be too heavy to run in a polled manner. I think the real problem here is your DB data schema. What you’ve created is typical SQL not mongo. Mongo operates best with de-normalization. Sure you could use aggregate or similar to perform a join of the information, but it would be better to store the job listing columns needed as well as the match data in the main notification table as well.

Perhaps as a quick fix/compromise, you could subscribe to just the notifications and when it changes (autorun) you call your method to gather the data.

That could work … let me implement and check

I’d definitely recommend you use cultofcoders:grapher for this, or at the very least reywood:publish-composite and then join this data on the client.

Don’t even need a package.

Your publish can publish the Notification, JobListings, ResumeDiscussion, and Match cursors, render your notification list, and use findOne for any of the other collections. findOne(_id) is just a hash lookup, so there shouldn’t be any performance implications.

Maybe you can subscribe in sequence… like this:

// onCreated
Meteor.subscribe("notificationsByUserId", Meteor.userId());

this.notifications = new ReactiveVar();

Tracker.autorun(() => {
    if (this.notifications.get()) {
        let jobIds = [];
        for (notification in this.notifications.get() ) {
            jobIds.push(notification._id);
        }
        this.subscribe('jobListingByIds', jobIds);
    }
});
// onHelper (some function)
Template.instance().notifications.set(Notification.find({ userId: userId }).fetch());
// server
Meteor.publish("notificationsByUserId", function (userId) {
    return Notification.find({ userId: userId });
}

Meteor.publish("jobListingByIds", function (ids) {
    return JobListenings.find({ _id: { $in: ids } });
}

PS: This is an example only

1 Like

Quick FYI-- passing Meteor.userId() as a method argument is insecure: you cannot rely on clients to behave nicely. IOW a client could put a different userId in there. Correct is to rely on the server to provide the ID of the current client. See https://docs.meteor.com/api/methods.html#DDPCommon-MethodInvocation-userId

This also goes for subscriptions.

3 Likes