[Worked Around Solved] getMeteorData() is slow

A component can mount long before the collection used in getMeteorData is ready. That’s why sometimes you will get undefined.

4 Likes

@sylar Could you try this? Not sure exactly if it would blow up but if you can nerf it until it’s ready that may work:

getMeteorData() {
    return {
      userProfile: Meteor.user().profile || {}
    };
},

still, people just ignore the edge case when user is not logged in or user collection is not yet on client.
So render something other when it is not defined, do not try to render that user in render() till it is defined.
Render some placeholder element instead.

1 Like

In general, in Meteor, we’ve gotta be careful info we want to be reactive. If we use Meteor.user() in a computation, then that will be reactive when absolutely any info inside the user object changes, which can be lots if the user object has lots of changing info.

I read somewhere (can’t find it at the moment) that if you want to react to a specific property of a document, you can do just that with the fields option, which will cause reactive updates less often and perform better:

Meteor.users.findOne(id, {fields: {"profile.foo": 1}}) // react only to changes on profile.foo

Additionally, we can throttle reactivity and limit it to, say, at most one update per 2-second time window:

let profileFoo = new ReactiveVar('') // initial value of empty string

let setProfileFoo = _.throttle(value => profileFoo.set(value), 2000)
                                     // ^ fire this logic at most once in any
                                     // given 2-second period of time.
                                     // See http://underscorejs.org/#throttle

Tracker.autorun(computation => {
    let foo = Meteor.users.findOne(id, {fields: {"profile.foo": 1}})
    setProfileFoo(foo)
})

class Foo extends React.Component {
    render() {
        // this render method will only fire at most once per 2 seconds.
    }
    getMeteorData() {

        // will only change at most once in any 2-second time window, and only on the profile.foo property!
        let foo = profileFoo.get()

        return {foo}
    }
}

This getMeteorData is “fast” now. :wink:

11 Likes

I’ve used fields before but I didn’t think about throttle… nice! :thumbsup:

5 Likes

Nope: Can we solve React cursor inefficiency? (vs Blaze)

Isn’t that the reason why TrackerReact does this:
https://github.com/ultimatejs/tracker-react/blob/master/tracker-react-mixin.js#L14

We use TrackerReact instead of MeteorGetData and, in case anybody tried, would be interested to know if there is any difference. We actually use it in production and render about 1000 items in virtual lists :?

4 Likes

Sorry for the absence. UK bed time called but Im awake now. I’ll go through the replies and see what works best. Thanks for all replies!

Edit:

I think I have not sent the message correctly. Why your solution will not work? I’m not rendering anything so I can not render something else while I wait on their data to be loaded in client. Maybe what I want can be done another way? I’m still learning Redux but for now I’ll try with pure React and Meteor.

I want to bind a user profile field. So far what I did only works in Chrome but getMeteorData() is slow in other browsers. The scenario is, when a user is on their profile page, all fields will be blank. Now they will fill in the blanks then hit update. Now…those fields have the value of what was entered and they can update at will.

So first they are presented with a blank field:

When already filled out and updated, they can still see what was inputted:

Should be able to make changes:

My work around this is this:

...

componentDidMount() {
  let p = this.data.user.profile;
  console.log(this.data.user); // load instant in chrome. Undefined in FireFox :frowning:
  if ( isMounted() ) {
    this.setState({fname: p.fname});
  } 
},

...

I really need the state so the fields return the values of what was saved instead of having it blank.

Now that field would look like:

<input type="text" value={this.state.fname} />

This will not work because the value is permanently set and cant be changed/updated:

<input type="text" value={this.data.user.profile.fname} />

even with fast-render, do not expect user collection to be available all the time.
So test if this.data.user is available inside render(), if not return placeholder dom element.
If it is available you can return your form.
Nothing is needed in componentDidMount(), that getMeteorData will take care of re-renders during all changes of user record.

What I need is, when this.data.user is ready then the state can be updated. Seems Meteor is not for me I’m wondering.

this already handle it
this.user.data is already state and every time something change there, it get re-rendered

Ok but how to have something like this, I know this wont work:

getInitialState() {
 return {
  fname: this.data.user.profile.fname
 }
}

I must have this.state.* because I want them to be able to change any values in a field.

TrackerReact looks amazing dude! What’s the catch? Is there a negative to using this instead of Meteor’s mixin?

Tried that too. Same issue fetching user data.

when you have your subscription in some higher component, than use that component to render form only when it is already ready and pass there props whatever you want and do not render that form component before user data are ready.

that way you can initiate your form state based on passed properties from parent component.

It’s automatically subscribed as it’s set to null:

Server:


Meteor.publish(null, function () {
  if (this.userId) {
    return Meteor.users.find({_id: this.userId},
                             {fields: {'regtype': 1, 'profile': 1}});
  } else {
    this.ready();
  }
});

that does not mean it is available during client startup (especially if you dont use fast-render)

Don’t copy this.data into state. Instead, use this.data as the default, and override with this.state like so:

myValue = this.state.myValue || this.data.myValue;

This way, the value will update, unless the user has overridden it.

1 Like

Fast-Render makes the userId immediately available. You should use that for auth checks, separate from the user collection. And if you’d want, you could check the user collection and block re-rendering in a componentShouldUpdate until it becomes available, i.e. combined with SSR.

@sylar maybe you want to check out TrackerReact. Here is a basic example, my only one public, that shows how you could deal with user and user authentication:

@sergiotapia we have not found any drawbacks. And since the package is literally just a few lines, we decided to bring it into production. On the repo was a issue discussion about that.

One of the benefits is that it brings meteors known plug & play, without freezing states, to react in meteor.

2 Likes

Ok ill try this and see. Thanks. But Im new to react and more studies needed.

1 Like