This package contains an example app that uses Meteor and React together. It demonstrates:
- Using the
react
package to integrate Meteor and React - Fetching data from reactive Meteor data sources using the
ReactMeteorData
mixin and thegetMeteorData()
method - Routing using
ReactRouter
- A basic UI using
Twitter Bootstrap
andReactBootstrap
- Login, change password, enrollment emails and user management using the Meteor accounts system
- ES6 syntax using the
Babel
transpiler (.import.js
files) and ReactJS' transpiler (.jsx
files) - ES6 modules using
universe:modules
- Tests using
Velocity
. - Typed collections / validation using
aldeed:collection2
. - Client- and server-side dependencies loaded from NPM and served with
browserify
. - JSHint configuration to allow editors like Sublime Text or Atom to warn about potential problems early
The app is very simple: it shows a ticking clock and allows the user to record
a timestamp with a name. Most of the sample app-specific logic can be found
in client/components/timestamps.import.jsx
and lib/models.import.js
, with
references from client/components/app.import.jsx
,
client/components/navigation.import.jsx
, client/js/routing.import.jsx
and
client/css/example.css
.
The various files have comments explaining their purpose and logic. At a high level:
client/
contains various React UI components and other client-side-only code. Most of these are relatively re-usable, for example the login, change password, enrollment and user management components.server/
contains server-only functionality such as application initialisation and methods.lib/
contains shared code such as model/collection definitions and publications.tests/
contains Velocity tests using the Jasmine test syntax. The tests there are examples generated by Velocity and pretty frivolous. The test runner can be found in the top right hand corner of the running app.
Meteor does not (yet!) have a good approach to modularising applications. In
short, all of the JavaScript code intended for the client is loaded into the
browser as <script />
tags, and so it is easy to end up with load-order
problems. Meteor does have a packaging system that supports dependency
management and imports, but it is quite ideosyncratic and requires that
any library found on e.g. NPMJS is wrapped into a Meteor package.
In this example, we have taken the following approach:
-
We have created a "private" package called
packages/app-deps
(usingmeteor create --package
). -
In the
package.js
file, we declare dependencies on published NPM modules using the standard Meteor API for this. When the package is built, this will cause Meteor to download the relevant modules. -
We use the
cosmos:browserify
package to runBrowserify
when the package is built to create a bundle that can be sent to the client. In the fileclient.browserify.js
(plus some relevant options inclient.browserify.options.json
), werequire
the relevant modules and put them into a global object calledDependencies
. We do something similar for server-side dependencies inserver.js
in the package. -
Dependencies are then exported using magic provided by
universe:modules
. Themain.export.jsx
file declares the exports, and thesystem-config.js
file allows imports like:import { _, Router } from 'app-deps';
We also use universe:modules
to allow ES6 module syntax in app code. Almost
all app-specific files are named with .import.jsx
or .import.js
syntax.
Within these files, we can use syntax like:
import Loading from 'client/components/loading';
In loading.import.jsx
, we have:
/* jshint esnext:true */
/* global Meteor, React */
"use strict";
import { _, ReactBootstrap } from 'app-deps';
var { Modal, ProgressBar } = ReactBootstrap;
export default React.createClass({
displayName: 'Loading',
render: function() {
return (
<div className="please-wait modal-open">
<Modal
title='Please wait...'
backdrop={false}
animation={false}
closeButton={false}
onRequestHide={() => {}}>
<div className='modal-body'>
<ProgressBar active now={100} />
</div>
</Modal>
</div>
);
}
});
To kick it all off, we have client/main.js
:
/* jshint: esnext: true */
/* global System */
"use strict";
System.import('client/routing');
This loads client/routing.import.jsx
, from which all other components are
imported.
Similarly, for server-side code, we have server/main.js
:
/* jshint: esnext: true */
/* global System */
"use strict";
System.import('server/startup');
And at the top of server/startup.import.js
:
import 'lib/models';
import 'server/admin';
This ensures that "global" code in these modules is run on startup.