Meteor is an open source web and mobile development platform, simplifying the process of building applications using only one programming language--JavaScript.
What is included in Meteor?
- Server: As a full-stack platform needs a web server, Meteor runs on top of Node.js, and we get all the benefits of using the Node, such as non-blocking I/O event driven architecture, an enormous amount of open source packages via NPM, plus we don't need to switch the language from the backend to the frontend.
- Database(s): Meteor comes with MongoDB in a universal way. We get a light copy of MongoDB on the browser called Minimongo. Both the databases are in sync in real-time out of the box, which means that the UI can be updated even before the data reaches the server, and that is one of the most compelling features of the platform.
- Communication: How do the client and server talk to each other? The communication is done via DDP (Distributed Data Protocol), which is an RPC (Remote Procedure Call) protocol built on top of WebSockets instead of HTTP. Due to the bidirectional communication nature of the web sockets, Meteor is a real-time platform by default.
- Frontend UI frameworks: Meteor was using Blaze as its primary template engine, which is Meteor's implementation of Handlebars; however, it also ships with support for Angular and React currently.
Now you must be wondering how all these technologies are glued together. On a very high level, the way Meteor works is that the server publishes channels and the client subscribes to those channels, then all updates from the client can update the server in real time and the other way around--updates on the server can update the client.
Now, let's look at what else is included in Meteor:
- There is also a native for Meteor package management, called Atmosphere, where you can find tons of useful packages. Note that all Atmosphere packages will be moved to NPM in the future.
- ECMAScript 6 or ES6 is the major upgrade of JavaScript language, and you can code in ES6 in any of your
js
files. There is no need for configuring anything like babel, webpack plugins, dev servers, and task runners. - Any changes on the server or the client create a build automatically for you, and there is no need for browser refresh or manual server restarts.
- Throughout the book, we will be using React expect for Chapter 8, Build a Chatbot with Facebook's Messenger Platform, where we will use Angular 2. With Meteor, there is zero effort to start writing React apps. There is no need for any babel presets or anything else. The support for JSX extension comes with the default ecmascript package.
In this chapter, we'll be looking at the following topics:
- Downloading and installing Meteor
- Installing packages with NPM and Meteor
- Overview of React's API
- Creating an example application with React and Meteor
Installing and running Meteor is extremely easy.
For Linux and iOS, all you need is the terminal and cURL. cURL is basically a command-line tool that we can use to move data with URL syntax, such as a GET, POST, and more, all directly from the terminal.
For Windows users, Meteor comes with an installer. You can download it from https://www.meteor.com/install">https://www.meteor.com/install. All the commands in the book are executed in a Linux terminal; just skip the sudo
command and execute the commands as administrator if you are on Windows.
If you don't have cURL installed, open your terminal and install it with the following commands:
- Run
update
as a superuser:
>> sudo apt-get update
- Install cURL:
>> sudo apt-get install curl
- Verify the installation:
>> curl --version
- Once you have cURL installed, installing Meteor is easy as it's a one line command:
>> curl https://install.meteor.com/ | sh
This will download the latest version of Meteor, and it will install it globally on your machine. Node.js, NPM, MongoDB, and all other default packages will be inside the .meteor
directory.
The final installation looks like this:
Meteor 1.4.4.2 has been installed in your home directory (~/.meteor). Writing a launcher script to /usr/local/bin/meteor for your convenience. This may prompt for your password.
In Chapter 9, Build Internet of Things Platform, we will build Node.js apps connecting to a Meteor app. If you don't have Node.js and MongoDB and you want to run them outside Meteor, here's a list of commands and installing scripts that you might find useful:
Install Node.js with cURL |
|
Uninstall Node.js |
|
Update node with NPM |
|
Update to specific version with NPM |
|
Check the version of Node.js |
|
Check the version of NPM |
|
Install cURL and check its version |
|
Uninstall cURL |
|
Purging cURL config data |
|
MongoDB install(Ubuntu) |
|
MongoDB version |
|
Stop MongoDB |
|
Restart MongoDB |
|
Remove MongoDB |
|
Remove Data Directories |
|
The following are the Meteor scripts:
Install Meteor |
|
Check the Meteor version |
|
Update Meteor to the latest version |
|
Check the Node.js version in Meteor |
|
Create a Meteor App |
|
Add an Atmosphere package to a Meteor app |
|
Remove Atmosphere package |
|
List all the Atmosphere packages |
|
Install an npm package |
|
List all the npm packages |
|
Run a Meteor app on a specific port; the default is |
|
Launch the MongoDB shell in a meteor app | in the app directory execute |
Show all databases from the MongoDB shell |
|
Switch to a database |
|
Show all collections in a database |
|
Reset Meteor app |
|
The steps for creating a meteor app are as simple as the installation:
- Open your terminal and change your directory to where you want to have your app installed. With the meteor command line interface (CLI), we can create an app with just one command:
>> meteor create <appname>
You can name it as anything you want; I am naming mine timerapp
. We'll go through the steps of creating a basic timer, updating the time from the server side to the client in real time:
>> meteor create timerapp
The meteor create appname
command installed all packages and libraries specified in the .meteor
folder in the app directory. In Meteor, the packages can be installed from Atmosphere and NPM, and there are two places where you can see what is installed by default.
Atmosphere packages are specified in ./meteor/packages
. All native npm packages' metadata is in the package.json
file.
Folders: If you go into your app directory, you will note that Meteor created three folders and two files: client
, server
, .meteor
, package.json
, and .gitignore
:
client
: This is for all client-side code.server
: This is for all server-side code..meteor
: This refers to all the core functionality of meteor: packages, databases, and many more.package.json
: This includes all NPM installed and saved packages..gitignore
: This will ignore before the commit the specified files.
cd
to that app directory:
>> cd timerapp
To start the server, run the following command:
>> meteor
After some installation logs in the terminal, you'll see something like this:
=> Started proxy. => Started MongoDB. => Started your app. => App running at: http://localhost:3000/
Note
You can also start the app with npm start
. This will execute the start script from package.json. "scripts": {"start": "meteor run"}
. You can start meteor in another port by passing port as argument, for example, $ meteor --port 2000 .
in case you have the 3000
port in use.
- Open the project in any text editor of your choice.
In my setup, I am using Atom https://atom.io/ with the Facebook package Nuclide https://nuclide.io/. There are other very good editors, such as Sublime Text, Visual Studio Code, WebStorm, Brackets, and many more.
The app skeleton, created by default, is nice and minimal. In the client and server folders, there is a startup JS main.js
file. All this comes by default, and you do not need to create additional configs, specify entry points of the application in different environments, dev servers, plugins, and so on.
The best way to start with React is by modifying and extending a Hello World example. There is a basic template available in the Facebook documentation.
For simplicity, you don't need a server to get started with React; you can load it and explore it as an HTML document by opening it in your browser.
Create a basic HTML page and place the scripts into the head
tag:
<head> <meta charset="UTF-8" /> <title>Hello World</title> <script src="https://unpkg.com/react@latest/dist/react.js"> </script> <script src="https://unpkg.com/react-dom@latest/dist/ react-dom.js"></script> <script src="https://unpkg.com/babel-standalone@6.15.0/ babel.min.js"></script> </head>
With the preceding scripts, we loaded react
and react-dom
and the third script--babel
--will make (transpile) our ES6 and JSX scripts compatible with the browsers.
Also, we can render our first component in the body
tag:
<body> <div id="root"></div> <script type="text/babel"> //this will make ES6 code and React Jsx compatible with the browser. ReactDOM.render(<h1>Hello, world!</h1>, document.getElementById('root')) </script> </body>
There are two ways to define components in React: as plain JavaScript functions or as an ES6 class that extends from React.Component
.
Let's look at a basic ES6 class component:
class Greeting extendsReact.Component { render() { return <h1>Hello, {this.props.name}</h1>; } }
In the preceding code, we have an ES6 class Greeting that extends from React Component class; then we defined a render method that returns HTML tags. HTML tags in JavaScript is the special thing that comes with React. In essence, it is JavaScript XML (JSX) that Facebook added to React to make it more descriptive, and it also saves typing.
You can use plain JavaScript but many developers, including myself, find it actually easier to write HTML into the JavaScript.
Next, paste the Greeting
component just above the ReactDOM.render
line:
class Greeting extends React.Component {
render() {
return <h1>Hello,
{this.props.name}</h1>;
}
}
ReactDOM.render(
<h1>Hello, world!
</h1>,document.getElementById('root')
)
If you save your file and refresh your browser, you'll see that nothing changed. To render a component, you need to attach it to an actual DOM element or add it to a parent component that can render it.
The idea of React is that we can build components or blocks and put them together to assemble complex user interfaces. Another thing you may find different is that the data flow between components is not that well prescribed like other frameworks. React itself has no actual strict pattern on how that should be done.
However, the intention is to have the data always flowing from top to bottom in one direction, or unidirectionally as many refer to it, like a waterfall. Top-level components can pass data to their child components in one direction; if you want to update the data of a child component, you'll need to update it in the parent and pass it down again. Let's see how this works.
Here's how we can render the Greeting
component that we created:
ReactDOM.render( <Greeting name="Joe"/>,document.getElementById('root') );
We added the Greeting
component as a <Greeting/>
element, and we also passed a parameter, called name
, with the value Joe
.
Save the document, refresh the browser, and see what it does.
The properties or the props
in the components are the way we pass data to components and the parameter name is accessible from the Greeting
component as this.props.name
.
If we want to render many instances of the Greeting
component, for example, we need a parent component that can render multiple child Greeting
components:
class App extends React.Component { constructor(props) { super (props); console.log(props); // array of three children components } render() { return ( <div> {this.props.children} // render the children components </div>) } } const Greeting = ({ name}) => ( <div>{name} </div> )
Here the parent App
component wraps and renders many children Greeting
components:
ReactDOM.render( <App> <Greeting name="Joe"/> <Greeting name="Tony"/> <Greeting name="Harry"/> </App>,document.getElementById('root'))
Besides the flexibility of decoupling your development with reusable components, another powerful feature of React is in its component state. Every React component comes with an internal state.
If the state of a component changes, the render method will fire and re-render the component. One thing that's very different from other frameworks is that React will re-render only the changes and nothing else.
Also, it does so in a very efficient way, and it doesn't have to touch the DOM for searching before making any changes. What is happening in the back is that JSX actually represents the DOM in JavaScript, and all the updates are happening on the script site in memory first; then it will do some checks, then batch the changes and, finally, it will commit them to the DOM.
What is a state in React's components?
Components can be declared as pure JavaScript functions, and they are called Stateless, such as this one:
function Greeting({ hello }) { return <div>{hello}</div>; }
Alternatively, you can write the preceding code as an ES6 arrow function:
const Greeting = ({ hello }) => ( <div>{hello}</div> )
Every component can hold internal encapsulated data, and we call this a state of the component. If you want to add a state to a stateless component, you should define it as an ES6 class.
Before adding an ES6 constructor and super methods to the stateless components, we can overview what an ES6 constructor is and what the super method does in ES6 classes.
In ES6, we can create a class, such as the following:
class Component { constructor(props){ this.props = props; } }
The names of the parameters and methods are named as React's once. This is not from the React library source.
We can add a method called render
, as mentioned in the following code:
class Component {
constructor(props){
this.props = props;
},
render() {
return this.props;
}
}
In an ES6 classical way, we can extend the base/parent class Component
:
class Greeting extends Component{ constructor(name){ super(name) // passing the value to the parent class which gets assigned to the props console.log(super.render()) // with the super method we can call the functions from the parent class console.log(this.render()) // or directly as this.render } }
In a classical way, we can create a new
instance of the Greeting
class, like this:
let greet = new Greeting('Joe'); console.log(greet.render()) // have an access to the parent method render
We can create a render()
method with the same name in the child class:
class Greeting extends Component{
constructor(name){
super(name)
this.name = name;
console.log(super.render())
}
render() {
return 'Hi ' + this.name;
}
}
let greet = new Greeting('Harry');
console.log(greet.render()) // child overrides the parent
Facebook and the React community encourage the use of composition over classical inheritance. It can be said that any other pattern to reuse code is considered anti-pattern in React. A lot more can be said about that, but React has been heavily used in production at Facebook without using inheritance for sure. In such a large-scale application, the advantage of building independent pieces and combining them to form a complex functionally is what made React so popular. Components can be moved around, organized by common functionality; they can be well tested and refactored without much of a risk of breaking other parts of the system.
Extending from the React.Component
:
class Greeting extends React.Component { constructor(props){ // ES6 class constructor super(props) // ES6 } render() { return <h1>Hello, {this.props.name}</h1>; //local } }
Instead of creating a new Greeting
instance, we are adding it as a <Greeting/>
tag:
ReactDOM.render( <Greeting name="Johny"/>,document.getElementById('root'))
Some components can be strictly presentational; others may not know who can be their children components in advance. Many namings later came from the community, Smart and Dumb components, Containers and Components, functional and classical.
How React is used it's all up to the developers, the team, and the organization. It's a very powerful and unopinionated library and can be used along with other frameworks.
One can say that React barely has any API. It has about 9-10 methods and that is all we get.
The State
is one of the main APIs in React. The best place to define the initial state of a component is in the class constructor.
Create a class component and initialize its state:
class Button extends React.Component { constructor(props){ super(props) this.state = {text: 'OFF'} this.handleClick = this.handleClick.bind(this) } handleClick(){ this.state.text === 'OFF' ? this.setState({text: 'ON'}) : this.setState({text: 'OFF'}) } render() { return ( <button type="button" onClick={this.handleClick}> {this.state.text}</button>) } }
This is an example of an internal state of a button that changes its text on user click (it toggles between ON
and OFF
).
A few things are required for that component to maintain a state
:
- We defined the state in the class constructor method. This is the first entry of the class that will be executed. We can think of that definition as the default state of the component.
- We created a
handleClick()
method and bound it to thethis
class in the class constructor. It will fire when the user clicks on the button. - In the
handleClick
method, we used one of the React's APIs--this.setState
--and based on the comparison of the current state, we set our new state.
Note
React is built with functional purity in mind, and if you set the state directly as this.state.text = 'OFF'
, the component will not re-render.
- In the button, we added a simple JavaScript event--
onClick
. Here, React has a slightly different syntax on events; it uses camel case.onClick
becomesonClick
.
The second form of the setState()
method is passing a function in the state instead of an object; in the callback, we get the previous state:
this.setState (function(prevState) { return { text: prevState.text === 'OFF' ? 'ON' : 'OFF' }; });
Alternatively, an arrow ES6 function can be used:
this.setState((prevState) => ({ text: prevState.text === 'OFF' ? 'ON' : 'OFF' }));
Other React methods.
A few other important React APIs are the component life cycles methods:
class Greeting extends React.Component { constructor(props) { super(props); console.log('constructor'); } componentDidMount() { console.log('componentDidMount'); } componentWillUnmount() { console.log('componentWillUnmount'); } render() { return ( <div> <p>{this.props.name}</p> {this.props.children} </div>) } }
Let's test these life cycle methods by adding a simple button to the body of the HTML:
<button onclick="umountComponent()">Unmount</button>
Also, we can define that function in the JavaScript tag, as follows:
function umountComponent(){
ReactDOM.unmountComponentAtNode(document.getElementById('root'))
}
When the app is loaded, the order of the events is as follows:
- Constructor method is called.
componentDidMount()
is called when the component is mounted to a parent or directly to the DOM.componentWillUnmount()
is called just before the component will unmount from the DOM.
A simple use of these events can be as shown:
- Constructor: Initialize the component's default state in the constructor.
componentDidMount()
: The component is ready. You can perform any actions at that point.componentWillUnmount()
: The component will be detached from the DOM; here, you can clear any resources that may cause memory leaks.
Starting from the client, let's create our simple app:
- Delete all the content of the client folder.
- Create the
index.html
file with the following content:
<head> <title>Timer</title> </head> <body> <div id="root"></div> </body>
- Meteor will take care of the missing HTML tags. It will compile it as a template rather than serve it as an HTML document.
- It will throw an error if you try to add the
<!DOCTYPE html>
andhtml>
tags:
While processing files with <cdpcomment data-comment-id="2521" data-comment-text="Sounds incomplete. ">">templating-compiler (for target web.browser): client/index.html:1: Expected one of: <body>, <head>, <template>.
Since we are using only React, we don't need to have the blaze-html-templates
package installed.
There are two ways to add and remove atmosphere packages in Meteor: directly add it in .meteor/packages
or use the meteor add
or meteor remove
commands.
We will remove blaze
and jquery
for this example:
>> meteor remove blaze-html-templates
This will result in the following terminal:
blaze-html-templates removed from your project caching-compiler removed from your project caching-html-compiler removed from your project templating removed from your project templating-compiler removed from your project templating-runtime removed from your project templating-tools removed from your project
Now if you run your app, you will note that nothing is loaded. You'll need to install the html-static
package to let Meteor load a static HTML file.
You can install it with the following command:
>> meteor add static-html
It will add all the needed dependencies:
caching-compiler added, version 1.1.8 caching-html-compiler added, version 1.0.7 static-html added, version 1.1.13 templating-tools added, version 1.0.5 Remove Jquery by deleting it in the package file (.meteor/packages) jquery@1.11.10 # Helpful client-side library
You can add and remove packages even if the server is running. Meteor will restart it automatically when a package is removed or added.
The next step is directories and files:
- Create
index.js
in the root of theclient
folder. This will be our start up file where we can import and render the rest of the components. - Create the
components
folder. For a small demo app like this, we can keep the components in afolder
components. - In
folder
components, create aTimer.js
file, you can use and alsoTimer.jsx
; Meteor will transpile JSX into JavaScript by default. Both ways are fine.
Install react
and react-dom
with npm
:
>> npm install react --save >> npm install react-dom --save
In the Timer.js
file, let's define our component:
import React from 'react';
import ReactDOM from 'react-dom';
class Timer extends React.Component {
constructor(props) {
super(props);
}
render() {
return (<div>
<p>Timer</p>
</div> )
}
}
export default Timer;
It is a class
component that will display the time pushed in real time from the server. The only difference between preceding examples and this one, is that we exported that component to use it in other components.
Now, let's import and render it in the index.js
file:
import React from 'react'; import {render} from 'react-dom'; import Timer from './components/Timer' render(<Timer/>, document.getElementById('root'))
First, let's create the functionality of the app on the client; then we can start pushing the data from the server.
Initialize the state in the constructor and set a new state every second:
constructor(props) { super(props); this.state = {time: 0}; } componentDidMount() { setInterval(() => this.setState({time: new Date().toLocaleString()}), 1000); }
Also, we have the following code in the render()
method:
render() {
return (<div>Time :
{this.state.time}</div>)
}
When the component mounts, we set a new state every second. This will force the component to re-render and display the latest state.
To exercise the idea of the state and the props, we can now create a child component that the Timer
component will render and pass its state as prop.
Create a child component in the components folder, called TimerDisplay
:
class TimerDisplay
extends React.Component {
constructor(props) {
super(props);
}
render() {
return
(
<div>Time : {this.props.time}</div>
)
}
}
export default TimerDisplay;
The TimerDisplay
will receive and render the data through the props.time
. It can be a stateless presentational component, and it can be defined as a function instead of a class:
import TimerDisplay from './TimerDisplay'; class Timer extends React.Component { constructor() { super(); this.state = {time: new Date().toLocaleString()}; } componentDidMount() { setInterval(() => this.setState({time: new Date().toLocaleString()}), 1000); } render() { return ( <div> <TimerDisplay time={this.state.time}/> </div>) } } export default Timer;
Here, the parent component, Timer
, re-renders every time its state changes, and then renders the child component, TimerDisplay
, by passing its state as props.
What will happen if we add another TimerDisplay
, but this time with a static value:
render()
{
return (
<div>
<TimerDisplay time={this.state.time}/>
<TimerDisplay time='today'/>
</div>)
}
To see the power of React in action, open the console in Chrome and press the Esc key then go to Rendering
and click on the checkbox Paint Flashing
. You will note that even if we rendered both TimerDisplay
, only the one that has its value changed is re-rendering.
In the preceding example, the data passed to the child component via props was generated in the parent component itself in the setInterval
function. In order to render React components on Meteor's data change, we need to create a component container.
The steps are as follows:
- Add
react-meteor-data
andreact-addons-pure-render-mixin
npm packages using the following command:
>> npm install react-meteor-data --save >> npm install react-addons-pure-render-mixin --save
- Import
createContainer
into theTimer.js
component:
import React from 'react'; import TimerDisplay from './TimerDisplay'; import { createContainer } from 'react-meteor-data';
- Export the container function instead of the component:
export default createContainer(() => { return { time: Time.find().fetch() }; }, Timer);
The first parameter is a callback
function that returns the result as an object named time
, and the second parameter is the Timer
component. The way it works is that on any changes in the browser database Minimongo, the data will be fetched and passed to the component as props (time in our case).
To break this down, refer to this:
- We defined
Time
as a MongoDB collection. find()
is a MongoDB method to query records (records are called documents in MongoDB) from the collection. If there is no query specified, will return cursor for the first 20 records by default.- Adding the
fetch()
method will return the documents as an array.
Meteor allows us to create a collection directly in the code:
Time = new Mongo.Collection('time');
You can access all the application data with the Meteor MongoDB shell.
Open another terminal, cd
to the app directory, and start the MongoDB shell.
>> meteor mongo
Show all the databases in MongoDB:
>> show dbs
The default database for the app is named meteor
; switch to the meteor
database:
>> use meteor
Display all collections in the database:
>>show collections
The show collections
command will not return any results.
The reason is that we created a collection on the client side only. This collection exists in Minimongo, and it is in the browser's memory. If we refresh the page, the collection and the data will be gone.
In order to create the same collection and persist data into MongoDB, we have to execute the exact same Time = new Mongo.Collection('time');
on the server. As soon as we do that, both the collections will be in sync and the data will be persisted into the database.
For now, we don't want to save the timer data on the server; we only want to take advantage of the Meteor server to client real-time communication.
The way we can get real-time updates from the server is through the Publishing and Subscribing messaging pattern of Meteor.
The server publishes an event and on the other side, the client subscribes to that event and listens to data changes.
On the server, in our case in the server/main.js
file, we can publish an event in a very simple way:
import { Meteor } from 'meteor/meteor';
Meteor.startup(() => {
// code to run on server at startup
Meteor.publish('time', function() {
...
});
});
The publish function takes two arguments: name of the collection and a callback function that will be executed on each subscription from the client.
On the client, we can subscribe to it as shown here:
Meteor.subscribe('time');
All updates from the server will push an update to Minimongo collection on the client. The subscriber can also pass a parameter as a second argument and/or have subscription handlers as callback functions:
Meteor.subscribe('time', id);
Also, the subscription handlers look like this:
Meteor.subscribe('time', { //called when data is availble in Minimongo onReady: function() { }, // called on error onError: function() { }, // called when the subcription is stopped. onStop: function () { } });
Here's the server-side code of the subscription:
import { Meteor } from 'meteor/meteor';
import { Random } from 'meteor/random';
Meteor.startup(() => {
// code to run on server
at startup
Meteor.publish('time', function() {
let self = this;
const newTime = () => {
let
id = Random.id();
let time = {
time: new Date().toString()
}
self.added
('time', id, time);
}
Meteor.setInterval(function() {
newTime();
}, 1000);
});
What we did is covered in the following points:
- We created a publisher with a collection called
time
. - We used one of the Meteor's timer functions,
setInteval()
, to call anewTime()
function every 1000 milliseconds or one second. - In the
newTime()
function, we created an ID of the document and the document as the current time. - We then called the
added
method, which will notify the subscriber that we added a new document to thetime
collection.
The client-side code is as follows:
Time = new Mongo.Collection('time');
class Timer extends React.Component {
constructor(props)
{
super(props);
Meteor.subscribe('time');
}
render() {
return
<TimerDisplay time={this.props.time}/>;
}
}
export default createContainer(() => {
return
{
time: Time.find().fetch()
};
}, Timer);
This is what we did here:
- We created a collection called
time
. The collection is created locally in Minimongo. If we don't create the same collection on the server, it will be only in the browser's memory. - In the class constructor, we subscribed to the dataset time published on the server.
- In the
render
method, we rendered theTimerDisplay
component as we passedtime
asprops
. - At the end, we created a Meteor React container that listens to data changes, fetches the latest data from the collection, and passes it down to the component as
props
.
In both the server and the client, we didn't provide an unsubscribing mechanism. For example, if you close the browser, the server will continue calling the newTime()
function indefinitely.
Meteor.publish()
provides some other useful APIs. When the client is subscribed for the first time, we can send a notification that the initial dataset was sent using this.ready()
. This will call the onReady
callback on the client.
We can also unsubscribe the client with this.stop()
or listen whether the client subscription is stopped.
The publisher on the server:
Meteor.publish('time', function() { let self = this; self.added('time', id, time); // notify if record is added to the collection time let interval = Meteor.setInterval(function() { newTime(); }, 1000); this.ready(); // notify that the initial dataset was sent self.onStop(function () { self.stop() console.log('stopped called') Meteor.clearInterval(interval); // clear the interval if the the client unsubscribed }); });
The subscriber on the client:
const handle = Meteor.subscribe("time", { onReady: function() { // fires when this.ready() called on the server. } }); handle.stop() // will call onStop(callback) on the server. handle.ready() // returns true if the server called ready()
The clientTimer
component:
Time = new Mongo.Collection('time');
const handle = Meteor.subscribe('time');
class Timer extends React.Component {
constructor(props) {
super(props);
}
shouldComponentUpdate() {
return this.props.handle.ready() && this.props.time.length > 0;
}
componentWillUnmount() {
this.props.handle.stop();
}
render() {
return <TimerDisplay time = { this.props.time }/>;
}
}
export default createContainer(() => {
return {
time: Time.find().fetch(), handle: handle
};
}, Timer);
Here, we passed the subscriber's handle as a prop to the component and in shouldComponentUpdate
, we check whether the publisher sent the first dataset and whether it has any records. If yes, it will render the imerDisplay
component.
When we unmount the component, we send a notification to the that we stopped the subscription.
We went over the foundation of Meteor and overviewed its core components. High-level databases, servers, package managers, and its communication protocol (Distributed Data Protocol). On a high level, we now understand how the platform works.
We installed Meteor and created a sample app with the Meteor CLI. Then, we went over the default app skeleton and some of the included Atmosphere and NPM packages.
On the frontend, we overviewed the basics of React, its data flow, and component architecture. Then, we created a sample real-time server to client communication app and got familiar with the core functionality of the Meteor Publish and Subscribing API.
In the next chapter, we will get more familiar with the Meteor Methods, Schema, and validation by building a simple shopping cart app, and we will continue exploring data handling and reactive rendering with React on the frontend.