[SOLVED] Undefined... Always undefined?

I’m relatively new to Meteor and one of the most frustrating things I’ve encountered so far is when I’m trying to load some initial data, I always get undefined. For example, users can make posts and then they can edit them. When they click an edit button, they’re taken to a separate edit page, but when I try to load the initial collection data it always comes back undefined.

  componentDidMount () {
    this.setState({
      test: this.getTestData()
    })
  }

  getTestData () {
    const temp = TestCollection.findOne({name: 'test_post_1'})
    return temp.post
  }

When componentDidMount fires off, TestCollection hasn’t finished loading. So how do I do this? Normally I would just do something like…

  const testing = this.getTestData()
  if (typeof testing === 'undefined') {
    return 'Loading...'
  } else {
    return testing
  }

I can’t do that if the user is loading in existing data that needs to be edited.

Here’s an example repo I created.

1 Like

Try wrapping your code in a Tracker.autorun function, sounds like your data is loading before the rest of your application is finished running.

Put it inside componentDidMount?

Tracker.autorun(function () {
// your code
});

  componentDidMount () {
    Tracker.autorun(() => {
      this.setState({
        test: this.getTestData()
      })
    })
  }

Just results in Uncaught TypeError: Cannot read property 'post' of undefined(…)

Hmm… does it return undefined if you start at the root of the object?

I’m not sure what you mean by “root of the object”. This is the exact code I’m using for the example.

“temp” in this case would be your object being returned from the server. I’d check start there to move backwards and see if it returns anything.

This seems to have done the trick:

  componentDidMount () {
    Tracker.autorun(() => {
      const test = this.getTestData()
      if (typeof test !== 'undefined') {
        this.setState({
          test: this.getTestData().post
        })
      }
    })
  }

Thank you for the help!

1 Like

If you don’t need the autorun after you have valid data, you can pass a param and call stop()


Tracker.autorun((c) => { 
   
  //after the setState, 
  c.stop()
})
1 Like

Isn’t this the exact reason for things like createContainer, and TrackerReact?

import React, { Component } from 'react';
import {createContainer} from 'meteor/react-meteor-data';

class MyComponent extends Component {
    constructor(props) {
        super(props);
    }

    render() {
        return <p>{testData.name}</p>;
    }
}

export default MyComponentContainer = createContainer(() => {
    return {
        testData: TestCollection.findOne({name: 'test_post_1'})
    }
}, MyComponent);

3 Likes

I second what @copleykj said. Try to avoid putting Meteor-specific stuff in your React components (keep them pure React). Use createContainer to handle Meteor stuff (Mongo collections, Tracker, etc) and pass results into your React component via the return statement.

I was using TrackerReact. It didn’t do anything for my specific use case.

Really? The very purpose of TrackerReact is to wrap your reactive data sources in autoruns like all the other examples, only in a more structured and automatic way…