Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Any plans or suggestions for no data and/or error handling? #6

Open
genyded opened this issue Apr 4, 2016 · 4 comments
Open

Any plans or suggestions for no data and/or error handling? #6

genyded opened this issue Apr 4, 2016 · 4 comments

Comments

@genyded
Copy link

genyded commented Apr 4, 2016

Right now when no rows match the query for Documents, the default Loading... component displays instead of any children. Do you have any plans to add support for no data and/or error handling? If not, I suppose the earnest is on us to handle that... but not sure how when all we get returned is Loading... if there is no matching data.

@SachaG
Copy link
Contributor

SachaG commented Apr 4, 2016

Good point. What would be the best way to handle this? Let the use pass a noResultsComponent to display when there's no data?

Note that for the ListContainer it's a bit different, since you can already test results.length to see if there are results or not.

@genyded
Copy link
Author

genyded commented Apr 4, 2016

Yes - List is good to go. For Documents, a no results component could work. It seems like the most important thing is knowing no data was returned though. Once that is there however it's implemented, devs can do whatever they want with that info.

@genyded
Copy link
Author

genyded commented Apr 7, 2016

Here is a proposed solution:

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

const Subs = new SubsManager();

const DocumentContainer = React.createClass({

  mixins: [ReactMeteorData],

  getMeteorData() {

    // subscribe if necessary
    if (this.props.publication) {
      const subscribeFunction = this.props.cacheSubscription ? Subs.subscribe : Meteor.subscribe;
      this.subscription = subscribeFunction(this.props.publication, this.props.terms);
    }

    const collection = this.props.collection;
    const document = collection.findOne(this.props.selector);

    // look for any specified joins
    if (document && this.props.joins) {

      // loop over each join
      this.props.joins.forEach(join => {

        if (join.foreignProperty) {
          // foreign join (e.g. comments belonging to a post)

          // get the property containing the id
          const foreignProperty = document[join.foreignProperty];
          const joinSelector = {};
          joinSelector[join.foreignProperty] = document._id;
          document[join.joinAs] = collection.find(joinSelector);

        } else {
          // local join (e.g. a post's upvoters)

          // get the property containing the id or ids
          const joinProperty = document[join.localProperty];
          const collection = typeof join.collection === "function" ? join.collection() : join.collection;

          // perform the join
          if (Array.isArray(joinProperty)) { // join property is an array of ids
            document[join.joinAs] = collection.find({_id: {$in: joinProperty}}).fetch();
          } else { // join property is a single id
            document[join.joinAs] = collection.findOne({_id: joinProperty});
          }
        }

      });

    }

    const data = {
      currentUser: Meteor.user(),
      ready: this.subscription.ready()
    }

    data[this.props.documentPropName] = document;

    return data;
  },

  render() {
    const loadingComponent = this.props.loading ? this.props.loading : <p>Loading…</p>
    const noData = noData ? noData : <p>No data found.</p>

    if (this.data.ready) {
      if(!this.data[this.props.documentPropName]) {
        return noData;
      } else {
        if (this.props.component) {
          const Component = this.props.component;
          return <Component {...this.props.componentProps} {...this.data} collection={this.props.collection} />;
        } else {
          return React.cloneElement(this.props.children, { ...this.props.componentProps, ...this.data, collection: this.props.collection });
        }
      }
    } else {
      return loadingComponent;
    }
  }

});

DocumentContainer.propTypes = {
  collection: React.PropTypes.object.isRequired,
  selector: React.PropTypes.object.isRequired,
  publication: React.PropTypes.string,
  terms: React.PropTypes.any,
  joins: React.PropTypes.array,
  loading: React.PropTypes.func,
  noData: React.PropTypes.func,
  component: React.PropTypes.func,
  componentProps: React.PropTypes.object,
  documentPropName: React.PropTypes.string,
  cacheSubscription: React.PropTypes.bool
}

DocumentContainer.defaultProps = {
  documentPropName: "document",
  cacheSubscription: false
}


export default DocumentContainer;

@SachaG
Copy link
Contributor

SachaG commented Apr 8, 2016

Thanks! But some of this is going to change anyway as I want to replace getMeteorData with something else. I've been looking at React Komposer, but I can't figure out how to make the component dynamic (see arunoda/react-komposer#69)

So maybe I'll end up using the "official" createContainer instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants