How do you create a loading indicator(preloader) in Meteor and React?


#1

I usually have this page structure:
return this.props.loaded ? <ActualPageComponent /> : <Loading />
So how do i properly check if my data is loaded and pass it to {loaded} props?
All i can think of are this options:

  1. Check actuall values of data ie Boolean(Comments.find().fetch())

  2. {loaded: Meteor.subscribe('comments').ready()}

  3. ComponentDidMount() { this.state.loaded = true }

I really like the first option but it does not work. Comments.find().fetch() always returns an array ([] at the page load and [object, object] few moments after) so it is impossible to check data readinnes because many times i have zero data to fetch. For example if post has no comments to it.


#2

Do you use redux? (-:
With redux I’m using react-redux-loading-bar, it’s very simple.


#3

Here’s a full page implementation I’m using:

// my Container (parts of it)
export default createContainer((params) => {
  const { campaignId } = params;
  const sub = subsManager.subscribe('campaign.one', campaignId);
  const loading = !sub.ready();
  const campaign = Campaigns.findOne(campaignId);
  ...
  return { loading, campaign, ...}
// my Page (parts of it)
export default class CampaignBucketsPage extends React.Component {
  render() {
    const {
      loading,
      error,
      campaign,
      ...
    } = this.props;
    if (loading) return <LoadingPage />;
    if (!campaign) return <ErrorBanner error="No Campaign" />;
    if (error) {
      return (
        <div>
          <CampaignTop currentTab="summary" campaign={ campaign } />
          <ErrorBanner body={error}/>
        </div>
      );
    }
    return (
      <div>
        <CampaignTop currentTab="buckets" campaign={ campaign } />


// my LoadingPage component
import React from 'react';
import Card from '../../../ui/components/elements/Card';
import _ from 'lodash';

export class LoadingCounter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { elapsed: 0, start: (props.start || new Date()) };
  }
  componentDidMount() {
    // componentDidMount is called by react when the component
    // has been rendered on the page. We can set the interval here:
    const tick = () => {
      // This function is called every 50 ms. It updates the
      // elapsed counter. Calling setState causes the component to be re-rendered
      if (!this.state.start) return;
      this.setState({ elapsed: new Date() - this.state.start });
      return;
    };
    this.timer = setInterval(tick, 100);
  }
  componentWillUnmount() {
    // This method is called immediately before the component is removed
    // from the page and destroyed. We can clear the interval here:
    clearInterval(this.timer);
  }
  getDuration() {
    // const seconds = moment().diff(this.state.start, 'seconds');
    const elapsed = Math.round(this.state.elapsed / 100);
    const seconds = (elapsed / 10).toFixed(1);
    if (seconds > 60) {
      // TODO auto-notify server w/ AJAX call?
      const min = (seconds / 60).toFixed(1);
      return <span className="text-danger">
        <i className="fa fa-warning" />&nbsp;
        {min} min
      </span>;
    }
    if (seconds > 20) {
      return <span className="text-warning">
        <i className="fa fa-warning" />&nbsp;
        {seconds} sec
      </span>;
    }
    if (seconds > 10) {
      return <span className="text-warning">{seconds} sec</span>;
    }
    if (seconds > 5) {
      return <span className="text-info">{seconds} sec</span>;
    }
    return <small className="text-info">{seconds} sec</small>;
  }
  render() {
    return this.getDuration();
  }
}

export default class LoadingPage extends React.Component {
  render() {
    return (
      <figure className="figure">
        <img
          src="/img/animated-bull-drawing.gif"
          className="figure-img img-fluid img-rounded"
          alt="Loading..." />
        <figcaption className="figure-caption text-xs-right">
          <p>Loading... <LoadingCounter {...this.props} /></p>
        </figcaption>
      </figure>
    );
  }
}
LoadingCounter.propTypes = {
  start: React.PropTypes.instanceOf(Date),
};

#4

Thank you for sharing.
Creating an component is a great idea. Also i never heard of subsManager. Going to try it out.