Composing React with Meteor and Tracker is just plain simple with Tracker.Component

What do you think about this method of Composition with React? Taken from the README on Tracker.Component

Composition

With Composition in React we mean the method to split up data management and pure rendering components, composition is actually a known method in mathematics, “the pointwise application of one function to the result of another to produce a third function” (ref. Function Composition).

Composition can be achieved with known methods (createContainer, TrackerReact and react-komposer) by passing your data management function to the compostion method which resolves in a method that takes your Component as an argument.

With Tracker.Component this can be achivieved with:

// main.jsx

import React from 'react';
import Tracker from 'tracker-component';

Models = new Mongo.Collection('models');
if (Meteor.isServer) {
  Meteor.publish('cars', brand => Models.find({ brand: brand }));
}

class Composition extends Tracker.Component {
  constructor(props) {
    super(props);
    this.subscribe('cars', this.props.brand);
    this.autorun(() => {
      this.setState({
        cars: Models.find({ brand: this.props.brand }).fetch()
      });
    });
  }
}

const Cars = ({ cars = [] }) => (
  <ul className="cars">
    {cars.map(car =>
      <li className="car">{car.brand} {car.model}</li>
    )}
  </ul>
);

const MainLayout = ({content}) => (
  <main>{content}</main>
);

if (Meteor.isClient) {
  ReactDOM.render(<Composition brand={ 'Volvo' }><Cars /></Composition>, document.body);
}

Results:


<body>
  <ul class="cars">
    <li>Volvo XC90</li>
    <li>Volvo V90</li>
    <li>Volvo V70</li>
  </ul>
</body>

6 Likes

Love it! Simple/clear implementation, easy to understand what’s going on on first glance, and it’s published on NPM…

SOLD!

So why is nobody else excited about this yet? Have you done any performance tests vs the other composition options?

2 Likes

I don’t think a lot of people knows about it yet, it was released earlier this week so that might be a reason. I haven’t done any performance tests yet, that would be great for the comparison actually, I’m curious if you have any ideas/tips on what to start with doing tests, I’m still a bit new to tests in Meteor 1.3.

It is not easy to understand for me, or maybe I missed some recent development. From the code, I can see that Cars is a function that returns a React spec tree. From the usage (<Cars />) one I must make the (possibly false) assumption that the Composition component will pass the info that the Cars function needs. I also don’t see where MainLayout is ever used (remove it perhaps?).

If the code was more explicit (less magic) it’d be easier to understand.

@timbrandin Would you mind explaining what’s happening in the example?

Hi Joe,

The implementation to being a Composing method is really easy, and I’ve implemented it just like how any other composition works, just have a look at ReactMeteorData’s createComponent:

This is basically the same as what we’re doing in Tracker.Component:

The one difference is that if you pass in multiple children into the composition all of them get the state as props:

<Composition brand={ 'Volvo' }>
   <Cars>Yeah! I have: `props.brand="Volvo"`</Cars>
</Composition>

This idea is heavily inspired by React Router so I guess it is a good choice?

import React from 'react';
import { render } from 'react-dom';
import { Router, Route, IndexRoute, browserHistory } from 'react-router';
import { Accounts, STATES } from 'meteor/std:accounts-ui';

import { App } from '../../ui/layouts/app.jsx';
import { Index } from '../../ui/components/index.jsx';

import { Hello } from '../../ui/pages/hello.jsx';
import { Admin } from '../../ui/pages/admin.jsx';
import { NotFound } from '../../ui/pages/not-found.jsx';

Meteor.startup( () => {
  render(
    <Router history={ browserHistory }>
      <Route path="/" component={ App }>
        <IndexRoute component={ Index } />
        <Route path="/signin" component={ Accounts.ui.LoginForm } formState={ STATES.SIGN_IN } />
        <Route path="/signup" component={ Accounts.ui.LoginForm } formState={ STATES.SIGN_UP } />
        <Route path="/hello/:name" component={ Hello } />
      </Route>
      <Route path="/admin" component={ App }>
        <IndexRoute component={ Admin } />
      </Route>
      <Route path="*" component={ NotFound } />
    </Router>,
    document.getElementById( 'react-root' )
  );
});

Otherwise you could always replace that default render with your own, full flexibility for you!

1 Like