FSM 4: Let’s build our first page!

Teagan Atwater
Fresh Start Meteor
Published in
4 min readSep 19, 2016

--

Welcome to Part 4 of Fresh Start Meteor, a guide that walks you through creating a new Meteor project using ES6, React, Redux, FlowRouter, and Sass. Feeling lost? Start with Part 1.

Great, so we’ve installed Meteor and our packages, and we’ve figured out our directory organization. But if you run your app and visit localhost:3000 you’ll see a blank page and a console error.

This error is because the default microapp included in a new Meteor install was built using Blaze, which we removed from our project! I know I said I wouldn’t make you follow any particular example, but to introduce the directory structure we built in the last article while practicing React and ES6, let’s replicate that microapp together anyway. Once we have that covered we’ll work with more generic examples.

When you look through your project right now, you’ll notice four files: client/main.html, client/main.js, client/main.css, and server/main.js. Only the first two contain any code.

With ES6, we don’t depend on Meteor’s default load order for our files because instead we import each file as we need it. Our first job will be to break down client/main.html into a layout, a page, and two components, add the corresponding logic from client/main.js, set up a route, and import everything to the root client directory where Meteor will find it.

Making the templates

In your layouts/ directory, create new directory called application/ with a file called ApplicationLayout.jsx inside, and into it paste the following JSX:

import React, {Component} from 'react';export default class ApplicationLayout extends Component {
render() {
return (
<div className='app'>
{this.props.page}
</div>
);
}
}

If you’ve written React with ES6 before this shouldn’t look any different, and it shouldn’t be a crazy leap from ES5 either.

Just in case, here’s what’s going down: First, we import React and React.Component from the NPM react package we added in FSM 2. Then we define a new React.Component instance that becomes the default export for the file (so we can import it later without curly brackets). The component’s name includes the type of component it is (in this case, a layout) so that when the file is open in your code editor you can distinguish between it and other files of the same name. The only function our component has is the render function, which returns an XML object. Note that in JSX we use className instead of the usual HTML class attribute because “class” is reserved in JS. Inside the XML we insert whatever object is passed as a prop called page to our component.

Now let’s create a new directory called welcome/ in your pages/ directory with a new file, WelcomePage.jsx and paste in the following JSX:

import React, {Component} from 'react';import Hello from '/imports/ui/components/hello/Hello.jsx';
import Info from '/imports/ui/components/info/Info.jsx';
export default class WelcomePage extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0,
};
this.incrementCounter = this.incrementCounter.bind(this);
}
incrementCounter() {
this.setState({
counter: this.state.counter + 1,
});
}
render() {
return (
<div>
<h1>Welcome to Meteor!</h1>
<Hello counter={this.state.counter}
incrementCounter={this.incrementCounter} />
<Info />
</div>
);
}
}

Don’t worry about the functions outside of render(), I’ll get to those in a minute. First, let’s go make those two components we’re trying to import, Hello and Info. In the components/ directory, make a new directory for each and paste the following JSX into components/hello/Hello.jsx:

import React, {Component} from 'react';export default class Hello extends Component {
render() {
return (
<div>
<button onClick={this.props.incrementCounter}>Click Me</button>
<p>You've pressed the button {this.props.counter} times.</p>
</div>
);
}
}

and this JSX into components/info/Info.jsx:

import React, {Component} from 'react';export default class Info extends Component {
render() {
return (
<div>
<h2>Learn Meteor!</h2>
<ul>
<li><a href="https://www.meteor.com/try" target="_blank">Do the Tutorial</a></li>
<li><a href="http://guide.meteor.com" target="_blank">Follow the Guide</a></li>
<li><a href="https://docs.meteor.com" target="_blank">Read the Docs</a></li>
<li><a href="https://forums.meteor.com" target="_blank">Discussions</a></li>
</ul>
</div>
);
}
}

An aside on name formatting: All template JSX files are named in upper camel case, all other JS(X) files are named in lower camel case. All (S)CSS and HTML files are named in hyphenated lowercase. All directories are named in underscored lowercase. This keeps everything easy to parse, valid when referenced in our code, and uniquely identifiable.

Tracing the logic

Awesome. Remember those functions we ignored earlier? Let’s go back to WelcomePage.jsx now:

...constructor(props) {
super(props);
this.state = {
counter: 0,
};
this.incrementCounter = this.incrementCounter.bind(this);
}
incrementCounter() {
this.setState({
counter: this.state.counter + 1,
});
}
...

As you would expect, the constructor() is called once when the page component is loaded and never again. In this example you’ll notice we make use of React’s state instead of Redux (we’ll get there, I promise!)

Also worth noting is that in order to be able to call this.incrementCounter we have to bind the incrementCounter() function to the WelcomePage component explicitly. This wouldn’t be necessary if we weren’t hoping to use this anywhere in the incrementCounter() function, but alas.

To quickly touch on the {this.props.X} you noticed in Hello.jsx, take a look at the Hello component in WelcomePage:

<Hello counter={this.state.counter}
incrementCounter={this.incrementCounter} />

As you can see, props are passed just like normal XML attributes, and can even be bound functions!

Cleaning up

Now let’s go back to the client/ files that came with our Meteor install. We’ve put everything in main.html into our layout, page, and components, so we can delete its whole <body>. We’ll leave the <head> though, because this is where you can add all your usual meta tags and favicons and the like down the road. We’ll deal with main.js in the next article!

You can help make this guide better by commenting when anything seems unclear, unhelpful, or plain wrong. Please be clear, detailed, and keep it positive! If you have suggestions for future articles or questions about the guide in general, feel free to tweet at me: @teaganatwater Thanks so much!

--

--