Problem loading data React + TrakerReact +FlowRouter


#1

My app has a list of polls and I am passing the data as following:

export default class App extends TrackerReact(Component) {
  constructor() {
    super();
    this.state = {
      subscription: {
        pollsData: Meteor.subscribe('polls', {sort: {createdAt: -1},limit: POLLS_PER_PAGE * pageNumber.get()}) //add this when removing autopublish and insecure
      }
    }
  }

  componentWillUnmount() {
    this.state.subscription.pollsData.stop();
  }
  pollsData() {
    return PollsData.find({}, {sort: {createdAt: -1}, limit: POLLS_PER_PAGE * pageNumber.get()}).fetch();
  }
  increaseLimit() {
    if(POLLS_PER_PAGE * pageNumber.get() < PollsData.find().count()) pageNumber.set(pageNumber.get() + 1);
    console.log(" LIMIT " + POLLS_PER_PAGE * pageNumber.get());
  }
  showMoreButton() {
    if(POLLS_PER_PAGE * pageNumber.get() < PollsData.find().count())
      return (
        <div className="load-more">
          <button className="btn btn-more" onClick={this.increaseLimit}>Load More</button>
        </div>
      )
  }
  render() {
    return (
      <div className="">
        <Polls polls={this.pollsData()} />
        {this.showMoreButton()}
      </div>
    );
  }
}

That works fine. Then, to show only one poll I have another component that looks very similar:

import React, { Component, PropTypes } from 'react';
import TrackerReact from 'meteor/ultimatejs:tracker-react';

import PollsData from '/imports/api/polls.js';
import ViewPoll from "./ViewPoll.jsx";

export default class ViewPollContainer extends TrackerReact(Component) {
  constructor() {
    super();
    const id = FlowRouter.getParam('id');
    this.state = {
      subscription: {
        pollData: Meteor.subscribe('singlePoll', id) //add this when removing autopublish and insecure
      }
    }
  }

  componentWillUnmount() {
    this.state.subscription.pollData.stop();
  }
  pollData() {
    const id = FlowRouter.getParam('id');

    return PollsData.findOne(id).fetch();  
  }

  render() {
    return (
      <ViewPoll poll={this.pollData()} />
    );
  }
}

This however is giving me an error. It says:
ViewPollContainer.jsx:46 Uncaught TypeError: Cannot read property ‘fetch’ of undefined
This refers to
return PollsData.findOne(id).fetch();

In Constellation, I can see the data for the poll I am trying to display and under Subscriptions (also in constellation) I can see singlePoll with the id as params.

Then I replaced PollsData.findOne(id).fetch(); with a poll object as

poll = {
  _id: this.props.id,
  question: "Color",
  options: [
    {
      option: "white",
      id: 1
    },
    {
      option: "black",
      id: 2
    }
  ]
}
return poll;

And it works. So it seems the problem is with the publish method. PollsData is:

import PollsData from '/imports/api/polls.js';

Meteor.publish('polls', () => {
  return PollsData.find();
});

Meteor.publish('singlePoll', (id) => {
  check(id, String);
  return PollsData.find({_id: id});
});

But even if I replace the subscription on the ViewPollContainer component to subscribe to polls. it doesn’t work, while it works on the App component.

I cannot find what is wrong, and I would appreciate suggestions to fix it.

I also tried to manage the data using data containers, but it didn’t work either.


#2

I solved the problem by using a data container.

import { Meteor } from 'meteor/meteor'
import { createContainer } from 'meteor/react-meteor-data'
import  ViewPollHolder   from '../ViewPollContainer.jsx'
import PollsData from '/imports/api/polls.js';

export default ViewPollContainer = createContainer((props) => {
  const handle = Meteor.subscribe('singlePoll', props.id)
  return {
    isLoggedIn: Meteor.user() !== null,
    isReady: handle.ready(),
    poll: PollsData.find().fetch()[0]
  }

}, ViewPollHolder);

PollsData.findOne(props.id).fetch() doesn’t work. It says fetch is not a function, so I use PollsData.find().fetch()[0] to retrieve the document (the only one that is returned in this case in a format similar to what I expected from PollsData.findOne(props.id).fetch() . It is a hack that works, but I don’t like it. I should add some validation that the document returned is the expected one before passing the data to the component.

The ViewPollHolder looks like:

import React, { Component, PropTypes } from 'react';

import PollsData from '/imports/api/polls.js';
import ViewPoll from "./ViewPoll.jsx";

export default class ViewPollHolder extends Component {
  isReady()  {
    console.log(this.props.isReady);
    if(this.props.isReady){
      console.log(this.props);
      return (
        <ViewPoll poll={this.props.poll} />
      )
    }
  }

  render() {
    return (
      <div>{this.isReady()}</div>
    );
  }
}

and then, that one renders the ViewPoll.

Please let me know why findOne(id).fetch() doesn’t work.


#3

OK. I am an idiot :slight_smile:

findOne(id) returns what I need. In this case it seems it’s no a cursor so that’s why findOne(id).fetch() doesn’t work.
I lost many hours trying to understand what was going on…