React Containers, Flow Router, and React Mounter

Greetings community!

I have been using Meteor for some time but I am relatively new to React and have been struggling for a few days with how flow router, react mounter, and containers all behave together. I’m trying to serve data to a page for individual documents in a collection based on a URL parameter. I’ve tested the router to make sure that the data being passed correct and I see expected results in the server console for the router but I’ve been unable to render the data back in the Channel component.

Channel.jsx

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { Meteor } from 'meteor/meteor';

class Channel extends Component {
  getContent() {
    return (
      <div>
          Something goes here

      </div>
    );
  }
  render() {
    return (
      <div className="componentWrapper">
        <a href="/">Back</a>
        {this.getContent()}
      </div>
    )
  }
}

ChannelContainer.jsx

import { Meteor } from 'meteor/meteor';
import { createContainer } from 'meteor/react-meteor-data';
import Channel from './Channel.jsx';

import { Tasks } from '../api/tasks.js';

export default ChannelContainer = createContainer( container => {
  const id = container.content.props.id;
  const channelHandle = Meteor.subscribe('tasks');
  const loading = !channelHandle.ready();
  const channel = Tasks.findOne(id);
  const listExists = !loading && !!channel;
  return {
    loading,
    channel,
    listExists,
  };
}, Channel);

Flow Router route

FlowRouter.route('/channel/:id', {
  name:'channel',
  action(params) {
    console.log('routerparams',params.id);
    mount(ChannelContainer, {
      content: <Channel id={params.id} />
    });
  }
});

tasks.js

if (Meteor.isServer) {
  // This code only runs on the server
  // Only publish tasks that are public or belong to the current user
  Meteor.publish('tasks', function tasksPublication() {
    return Tasks.find({
      $or: [
        { private: { $ne: true } },
        { owner: this.userId },
      ],
    });
  });
}

It’s entirely possible I am misunderstanding the relationship between containers, components, and routers so if there is recommended reading for that, I would appreciate any help. However, it would also be great to explain where I’m going wrong here. In advance, thank you so much!

TheMeteorChef is a great resource to find out howto’s.
In this case, take a look at https://themeteorchef.com/snippets/using-react-komposer/

Thanks for the response, azrael188. That covers react-komposer, not createContainer. There’s a separate article on createContainer but it still doesn’t cover the relationship between react-mounter and createContainer.

Does anyone have any advice specific to this example?

Thank you again!

Find myself wondering the same thing it seems.

I have react-mounter, not sure how to use it with createContainer.

Hi,

not sure if it still is of some use. I was also looking around for hours to get this topic right. Here is my (so far the best I can think of) solution:

AppLayout (This is my main template. The “main” prop is given by the router):

import { Meteor } from 'meteor/meteor';
import React from 'react';
import { createContainer } from 'meteor/react-meteor-data';

import Header from '../components/Header';
import Footer from '../components/Footer';
import '/imports/stylesheets/style.css'

class App extends React.Component {
  constructor(props) {
    super(props);
  }

  render(){
    const {main} = this.props;
    return(
      <div className="Site">
        <Header />
        <div className="Site-content">
          <div>{main}</div>
        </div>
        <Footer />
      </div>
    )
  }
}

App.propTypes = {
  main: React.PropTypes.object.isRequired,
};

export const AppLayout = createContainer(props => {
  // props here will have `main`, passed from the router
  // anything we return from this function will be *added* to it
  return {  };
}, App);

Routing:

FlowRouter.route('/', {
  name: 'Main',
  action() {
    mount(AppLayout, { main: <HelloWorldContainer/> });
  },
});

And last but not least the HelloWorldContainer:

import React, { Component } from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import { Meteor } from 'meteor/meteor';

export default class HelloWorld extends Component {
  render() {
    return (
      <div className="ui segment">
        <h1>Hello World</h1>
        <h2>{this.props.user}</h2>
        <div className="two inline fields">
          <a href="/login" className="ui button">Log In</a>
          <a href="/join" className="ui button">Join</a>
        </div>
      </div>
    );
  }
}

export const HelloWorldContainer = createContainer(props => {
  return {
    user: Meteor.user() === undefined ? "" : Meteor.user().username,
  };
}, HelloWorld);

So basically I figured, that react-mounter only accepts stateless components (like in: react-mounter)

The trick is to wrap your component in a container with createContainer which is recommended anyways. If you are wondering why I export the HelloWorld as default and use the the container in the router: This has to do with my lazyness when importing pages in different layouts. You could also export the container as default.

1 Like