Is calling Meteor.user() in many helpers optimal?

I am calling Meteor.user() in many helpers, one helper for each field with one call to Meteor.user()
Is that optimal? I mean is that fast? wont it cause unnecessary re-renders?
I am also calling the same helpers from other templates that inherit the helpers using the following
aldeed:template-extension package.

Template.Layout_protected.helpers({
  userPicture() {
    let user = Meteor.user();
    return user && user.picture;
  },
  userFirstName() {
    let user = Meteor.user();
    return user && user.firstName;
  },
  userLastName() {
    let user = Meteor.user();
    return user && user.lastName;
  },
  userEmail() {
    let user = Meteor.user();
    return user && user.services && user.services.facebook && user.services.facebook.email;
  },
  userCreatedAt() {
    let user = Meteor.user();
    return user && ( user.createdAt.getDate() + '/' + (user.createdAt.getMonth()+1) + '/' + user.createdAt.getFullYear() );
  },
  languages() {
    return _.keys(TAPi18n.getLanguages());
  },
  isActiveLanguage(language) {
    return (TAPi18n.getLanguage() === language);
  },
});

You may want to consider defining a single helper returning one object with all properties.

In your template you could then do something like

{{#with allThings=myNewHelper}}
  <span>{{allThings.userFirstName}} {{allThings.userLastName}}</span>
{{/with}}
1 Like

I think the the real question is if we call Meteor.user() multiple times in a page/component, then does Meteor optimize this function? for example get the data from cache or something.

Meteor.user eventually executes this:

so caching is limited to another function call on the current context. The database is used as a last resort. However, that still means a minimum of three call stacks each time Meteor.user() is called.

3 Likes

How can I do that with #let instead of #with?

You need to be careful you don’t end up calling the helper multiple times, so not this:

{{#let firstName=myNewHelper.userFirstName lastName=myNewHelper.userLastName ...}}
  ...
{{/let}}

but this:

{{#with allThings=myNewHelper}}
  {{#let firstName=allThings.userFirstName lastName=allThings.userLastName ...}}
    ...
  {{/let}}
{{/with}}

Although, I don’t know that {{#let}} is in any way better or clearer than {{#with}}.

It is actually quite simple to optimize your code here. Since the accounts package already includes the default {{currentUser}} helper you can use it inside of a {{#let}} or {{#with}} for cacheing. This will work great if you just want to access the properties on the user record. For other methods where you need to compute a value based on those values you can create helpers that take the user as an argument and return this data, but my preferred method is to have the users collection return User models that have these methods defined on them.

{{#let user=currentUser}}
   {{user.firstName}} {{user.lastName}} {{user.picture}} {{userCreatedAt user}}
{{/let}}

If you would like to go the Model route, I highly recommend my package socialize:user-model.

This will require a little more work, but really pays off in the long run.

import { User } from 'meteor/socialize:user-model';
import SimpleSchema from 'simpl-schema';

// Attach new fields to the schema since socialize:user-model has already attached a schema
User.attachSchema(new SimpleSchema({
    firstName: {
        type: String,
    },
    lastName: {
        type: String,
    },
    picture: {
        type: String,
    },
}));

// Add the methods we need onto the User class
User.methods({
    fullName() {
        return `${this.firstName} ${this.lastName}`;
    },
    email() {
        return this.services && this.services.facebook && this.services.facebook.email;
    },
    createdDate() {
        return `${this.createdAt.getDate()}/${user.createdAt.getMonth()+1}/${user.createdAt.getFullYear()}`;
    },
});

Now you can use them in your templates like so…

{{#with currentUser}}
     {{fullName}} {{email}} {{createDate}}
{{/with}}

http://blazejs.org/guide/spacebars.html#Each-and-With

There are also two Spacebars built-in helpers, {{#each}}, and {{#with}}, which we do not recommend using (see prefer using each-in). These block helpers change the data context within a template, which can be difficult to reason about.

{{#each ... in}} is a looping (iterating) construct. If there’s nothing to iterate (no array or cursor), it won’t work.