Meteor + React Design Pattern: Mixins v. Composable Components


#1

I’m hoping to start a discussion about the best design pattern for using React with Meteor. I’m about to start a new project and I have to decide between using mixins, decorators, or composable components.

Here’s a pretty good introduction.

Any thoughts? What is everyone else using?

There’s also this discussion here.


#2

I would vote for ClojureScript approach :smiley: After playing with reagent for a while, it seems as best system. Kinda Immutable structures for the win.
From speed etc point of view, but the view and data separation question I am not sure

Other than that it would be separating data manipulation to reducers from your list.


#3

Nice article Sam! I just posted this on the github issue as well.

Here’s an idea I had to use a higher order component similar how most flux libs and Relay add data.

It’s basically a higher order function that takes in params and wraps your component with another using the ReactMeteorData mixin.

However I abandoned the idea after having an issue with Tracker not working and since i’m using Redux and flux now I didn’t have time to finish it.

let PostsList = React.createClass({
  propTypes: {
    posts: React.PropTypes.object,
    isReady: React.PropTypes.bool,
    increaseLimit: React.PropTypes.func,
  },
  render() {
    return (
      <div>
        {this.props.posts.map(postDoc => {
            <Post {...postDoc} />
        });}
      </div>
    );
  }
})
//
// wrap component with data wrapper
//
PostsList = Meteor.ReactData(PostsList, {
  increaseLimit(limit = 5) {
    this.limit = limit;
  },
  getMeteorData() {
    const sub = Meteor.subscribe('posts', this.limit);
    return {
      isReady: sub.ready(),
      posts: Posts.find().fetch(),
    }
  }
});

#4

Thanks!

Are you sure using the Mixin is a good idea? It seems like Mixins are going by the wayside.

Also, I’ve found myself overwhelmed with the different React libs. What was it that drew you to Redux?


#5

I think using a higher order function like yours is ideal, but if MDG wanted to re-use the mixin logic, using the inside the Meteor.ReactData (in the example) would be do-able.

I’m kind of on the fence with sideloading minimongo data and having flux/redux store that data. The pro with the latter is that all the data is in once place… unfortunately that means there’s two copies in memory. You can still use flux/redux for local state, replacing Session and reactive dicts.

At first the time-travel debugging drew me in but once I dug into it the concepts are really nice.

They’re based off functional programming where you’re never mutating state (just making a copy and mutating that). It gets rid of all the events and just uses simple functions. It borrows the Elm language concept of having 1 tree of state for the whole app. This makes it really nice for React because it breaks down the data flow into a simple mental model.

Here’s a quick overview of the lifecycle. You can even log out all dispatches when debugging!

// in the UI somewhere

store.dispatch( selectPlayer(this.props._id) );


// func creates a meta data object.. aka 'action' and returns it
//
function selectPlayer(playerId) {
  let player = Players.findOne(playerId);
  let playerName = player.name || "N/A";

  return {
    type: 'SELECT_PLAYER',
    playerId: playerId,
    playerName: playerName
  };
}


// reducer function merges old state (copy) with new changes based on action meta data
//
Reducers.players = function todos(state = {}, action) {
  switch (action.type) {
    case 'SELECT_PLAYER':

      let newState = merge(state, {
        selectedId: action.playerId,
        selectedPlayerName: action.playerName
      });

      return newState;

    case 'REMOVE_PLAYER':
      // .....
      return newState;
    case 'HIDE_PLAYER:
      // .....
      return newState;
  }
};


// then the store can be consumed to get new data, note that the players reducer
// is merged with global app state. you can combine serveral reducers (like todos, musicPlayer)
// to separate domains of state but still have one tree like below
//

store.getState() >>>   // result in console
{
  players: {
     type: 'PLAYER_SELECTED',
     playerId: '3jdhdkjfd7d8f3',
     playerName: 'Nikola Tesla',
  },
  todos: [
    {_id: '1d2g3', text: 'Learn Redux'},
    {_id: '3h2g7', text: 'Take over the world'}
  ],
  forms: {
    create: "How now brown c",
    email: "sally@gmail.com"
  },
  musicPlayer: {
      foo: 1,
      bar: 2
  }
}


// or hookup to a React view with react-redux higher order component
// gets data via `this.props`
//

let AppContainer = React.createClass({
  render() {
    return (<App {...this.props} />);
  }
});

this.AppContainer = connect(mapStateToProps)(AppContainer);