Any decent createContainer examples

I am trying to undertake what should be a simple task of rendering a React component which handles some data. This requires a component container to be created, do actually handle the data part. Could someone comment if I doing this the correct way?:

DataTableContainer.js:

import { Meteor } from 'meteor/meteor';
import React from 'react';
import { createContainer } from 'meteor/react-meteor-data';
import DataTable from '../../../imports/ui/components/DataTable.js';

export default DataTableContainer = createContainer(({ params }) => {
    const dataHandle = Meteor.subscribe('lists');
    const dataIsReady = dataHandle.ready();
    return {
        dataIsReady,
        data: dataIsReady ? dataHandle.find() : []
    };
}, DataTable);

DataTable.js:

import { Meteor } from 'meteor/meteor';
import React from 'react';

export default class DataTable extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            data: props.data
        };
    }

    componentDidMount(){
        var self = this;
        $('#listTable').dataTable({
            "sPaginationType": "bootstrap",
            "bAutoWidth": false,
            "bDestroy": true,
            "fnDrawCallback": function() {
                self.forceUpdate();
            }
        });
    }

    componentDidUpdate(){
        $('#listTable').dataTable({
            "sPaginationType": "bootstrap",
            "bAutoWidth": false,
            "bDestroy": true
        });
    }

    render(){
        var x = this.state.data.map(function(d, index){
            return <tr><td>{index+1}</td><td>{d.ma_lop}</td><td>{d.ten_mon_hoc}</td></tr>
        });
        return (
            <div id="listTableBlock" className="table-format-1">
                <table id="listTable">
                    <thead>
                        <tr className="success">
                            <td>col1</td>
                            <td>col2</td>
                            <td>col3</td>
                        </tr>
                    </thead>
                    <tbody>
                        {x}
                    </tbody>
                </table>
            </div>
        )
    }
};

List.js:

import React from 'react';
import DataTableContainer from '../../../imports/ui/components/DataTableContainer.js';

export default class List extends React.Component {

    render(){
        return (
            ...
            <DataTableContainer />
            ...
        )
    }
};

I think the problem here is that you are doing dataHandle.find(). Subscription handles donā€™t give you access to the data directly, you need to get the data from the collection that the subscription publishes to. So if you have a collection called Lists, it could be Lists.find().

Other than this, is this the correct way to define a container, s the Meteor guide doesnā€™t eaplain this very well.

You can simplify it even more if you like:

export default createContainer(() => { ... }

Also putting your props into state seems a bit ā€œoverkillā€ as props are reactive too, so thereā€™s no reason to use this.state.data when you could use this.props.data. It also makes it clearer (IMO) where the data is coming from that way too.

ok, so where is the name of the container defined? At present, I am calling <DataTableContainer /> in my List component, so Iā€™m guessing I need a name for it (?)

The fact that you would be using:

export default createContainer(() => { ... }

Means that the container is exported as the default export. That means that when you import you simply import the file with any name you like, so long is that is how it is then referred to in that file:

import AnythingYouLike from '/containers/DataTableContainer.js'

render() {
  return <AnythingYouLike />;
}
1 Like

Also, you donā€™t need to import Meteor into your presentational component files. They donā€™t need to know itā€™s a Meteor app.

Excellent - this was the missing bit of information I needed. Is this documentaed anywhere?

Itā€™s not a Meteor thing, itā€™s a Javascript thing, so yeahā€¦ Lotā€™s of places, but my personal go-to for JS docs:

So, where I am stating that the container wraps DataTable (above), is there a way to be able to pass this value in, so that this container can be re-purposed and pass data to whichever presentation container I give it?

People usually donā€™t do this - imagine that your data container is just a very simple wrapper that injects data into a specific component. If you are doing heavy lifting in the container function and want to share that, Iā€™d factor it out into a JavaScript module rather than trying to make the container work with multiple children.

Question, if there is a one-to-one relationship between the container and the component, why not have the component get the data itself?

So that both parts are easier to test, and itā€™s clear which parts of your code depend on the global data and which donā€™t. You can also easily determine why your component is rerendering by logging from the container. If that idea doesnā€™t appeal to you, there are plenty of packages out there that just make Reactā€™s render reactive.

You can read about all of the philosophy here: http://guide.meteor.com/ui-ux.html#components

2 Likes

Because the idea is to keep components as pure and reusable as possible.

I highly recommend this article and this one as well.

1 Like