React may be an awesome front-end library, but it can’t do much unless you give it some data to work with.

This is where data loading comes in. And until the promised GraphQL/Apollo future arrives, for now this means subscriptions.

And while chatting with Josh Owens the other day, he pointed out there are actually quite a few ways to deal with this issue in Meteor, all with their pros and cons. So today, I thought I’d take a look at four different approaches.

The Goal

Our goal will be fairly simple: display a single post.

To do this, we’ll use the container pattern where a “smart” component (the container) loads the data and then passes it on to a “dumb” component that only displays it.

Technique 1: the getMeteorData mixin

This is Meteor’s current default method. Basically, it lets us define a special reactive getMeteorData function where all your reactive dependencies live, and get a special this.data object in return (whose contents we’ll then pass on to our “dumb” component):

const PostContainer = React.createClass({

  propTypes: {
    postId: React.PropTypes.string.isRequired
  },

  mixins: [ReactMeteorData],

  getMeteorData() {
    const subscription = Meteor.subscribe('post', this.props.postId);

    return {
      ready: subscription.ready(),
      post: Posts.findOne(this.props.postId).fetch()
    }
  }

  render() {
    return <Post ready={this.data.ready} post={this.data.post}/>
  }
});

This works well, except that React mixins are on their way out. So for that reason alone, it might be a good idea to consider a couple alternatives.

Technique 2: TrackerReact

TrackerReact is a community package that lets you treat React a little bit more like Blaze, and inject reactive dependencies anywhere in your components.

As the documentation states:

Using TrackerReact instead you are no longer required to “freeze” all your reactivity in a single method. Any reactive data sources (e.g: collection.find() or Session.get('foo')) used in your render method […] are automatically reactive!

Here’s the same container using TrackerReact (except this time written with an ES6 class):

class PostContainer extends TrackerReact(React.Component) {

  constructor() {
    super();
    const subscription = Meteor.subscribe('post', this.props.postId);
    this.state = {
      ready: subscription.ready(),
      subscription: subscription
    }
  }

  componentWillUnmount() {
    this.state.subscription.stop();
  }

  render() {
    const post = Posts.findOne(this.props.postId).fetch();
    return <Post ready={this.state.ready} post={post} />
  }
});

Notice how we’re able to call Posts.findOne() right from the render method, instead of having to contain it inside getMeteorData?

While we do get cleaner and simpler code compared to using the getMeteorData mixin, TrackerReact’s benefits are a bit limited when using the container pattern since you’ll want to centralize your reactivity in a single component anyway.

On the other hand, if you’re not using that pattern (although you probably should!) then the ability to seamlessly call Session.get(), Collection.find(), etc. anywhere in your React code (just like you can in Blaze) can be a nice productivity boost.

Technique 3: React Komposer

React Komposer is another community package that aims to solve the data loading question.

But unlike the previous two methods, it uses a much more elegant pure-JavaScript composition approach:

import { composeWithTracker } from 'react-komposer';
import Post from '../components/post.jsx';

function composer(props, onData) {
  const subscription = Meteor.subscribe('post', props.postId);

  if (subscription.ready()) {
    const data = {
      ready: true,
      posts: Posts.findOne(props.postId).fetch()
    }
    onData(null, data);
  } else {
    onData(null, {ready: false});
  }
}

export default composeWithTracker(composer)(Post);

We can now use our newly created container by importing it and then using it just like a regular React component:

import PostContainer from './containers/post'
import ReactDOM from 'react-dom';

Meteor.startup(() => {
  ReactDOM.render((<PostContainer postId={'myPostId'}/>), document.body);
});

While React Komposer might seem a bit convoluted at first, one of its big advantages is its flexibility. As the documentation mentions, Komposer can work with multiple data layers:

This is a universal project and it works with any kind of data source, whether it’s based on Promises, Rx.JS observables or even Meteor’s Tracker.

Technique 4: React Document Container

Now here’s my own personal take on this problem. Seeing how often I was bumping into this same issue, I decided to build a set of list & document containers that sit on top of the data loading layer.

So although the containers currently still use the getMeteorData method, they could easily (and transparently) be ported to either TrackerReact or React Komposer.

In this case, we want to display a single item so we’ll use DocumentContainer:

<DocumentContainer 
  collection={Posts}
  publication="post"
  selector={{_id: myPostId}}
  terms={{_id: myPostId}}
  loading={<p>Loading…</p>}
>
  <Post>
</DocumentContainer>

We’re telling the container to subscribe to the post publication using the {_id: myPostId} subscription terms, then get the result from querying for the {_id: myPostId} selector on the Posts collection.

Once the data is loaded, the container will then pass on two props to our Post component: currentUser and document (which will contain the post).

As you can see, using a pre-built container abstracts away a lot of the complexity of fetching data.

And once Apollo comes out, it shouldn’t be too hard to just switch out this container for one that works with the new data layer.

Conclusion

So which method is right for you?

If you’re not quite comfortable with ES6 classes and want to take things slow, you can still use getMeteorData (as long as you’re aware it’s probably not going to stick around forever).

If on the other hand you want to follow the latest best practices, I would recommend using React Komposer. This is what Mantra recommends, and it’s quickly becoming the new standard.

If you’re not sure about the container pattern, or just prefer a more Blaze-like approach, then TrackerReact might be worth considering.

Finally, if you just don’t want to worry about all this stuff then using the List & Document Containers might be the way to go!

The excellent Meteor Guide hasn’t yet been updated to cover React, but once it has I’m sure it’ll point out the best solution. Until then, I hope this article will help you figure out the technique that’s best for you!