Meteor 1.3 With ReactJS
Facebook is eating up the world with Relay, Reflux, GraphQL, React, React Native, and bunch of other libraries to make scalable apps.
But this tutorial will just cover React with Meteor.
Goal
The goal of this tutorial is to guide you through the path of creating a Meteor app with React leading the V in the MVC, so what better example than recreating the Materialize CSS Starter Template?
The full code lives in this repo.
Creating the Project
First, create the project.
meteor create --release 1.3-modules-beta.11 react
cd react
rm -rf *
Then, run the project
meteor
I'm going to use materialize for the CSS, and Flow router for the Routing.
meteor add kadira:flow-router poetic:materialize-scss fourseven:scss
Next let's make our Meteor project NPM-ready.
Installing necessary dependencies
Create the package.json and install necessary dependencies.
npm init --yes # shortcut to create a package.json
Then fill the package.json with the current info
NOTE: It's very important that you fill in the basic info of the package, more specifically the name of your module. If you don't do this, you will get the next error when you try to install dependencies.
npm WARN install Refusing to install hapi as a dependency of itself
Then you can run npm install
to get the necessary dependencies to be used.
npm install --save --save-exact react react-dom react-mounter
Why use npm instead of a community React package?
You may be wondering, why are we installing React plugins using npm instead of just using a cool react package. Well, this was not something I said, this is something that @yyx990803 (evan yuu) says on this issue
.
One package to take in account here is the React-mounter package from kadira (yes, the guys behind all the awesome packages out there for Meteor) and from the README.md
, this is basically what this package does for you:
React Mounter lets you mount React components to DOM easily.
Starting the basic folder structure
The folder structure is always something to worry about when you start a new project. We are always trying to improve our code, and with the way we structure it on Meteor, there is not too much deal about folder structure. We have a /client and a /server folder (there is always a /public, /private /lib folder), so in this tutorial we are going to follow the Meteor standard, with some obvious folders in the /client folder, like /components.
Preparing the App Layout
Having something similar like this, the first thing we need to do is create our layout component, which will be inside react/client/layouts/mainLayout.jsx, and write up the following code.
import React from 'react';
export const MainLayout = ({navbar, content, footer}) => (
<div>
{navbar}
<div className="container">
{content}
</div>
{footer}
</div>
);
This will be our MainLayout component, looks simple right? It's just a const
exporting a MainLayout component, which accepts 3 parameters, navbar, content and footer.
If you are familiar with the Iron-router flow or BlazeLayout or React Layout package, the following will look pretty familiar.
<template name="MainLayout">
<div>
{{> navbar}
<div className="container">
{{> yield}}
</div>
{{> footer}}
</div>
</template>
Now we should create a router.jsx file to handle the main layout using FlowRouter, go ahead and create the .jsx file in this path. react/client/routing/router.jsx, then write the following code.
import React from 'react';
import {mount} from 'react-mounter';
import {MainLayout} from '/client/layouts/mainLayout.jsx';
FlowRouter.route("/", {
action (){
mount(MainLayout)
}
});
At this point, you should be already familiar with the import thing. We are just requiring React, Mount, and MainLayout from other folders in the App, but wait... you may be wondering, why are some ** requires** wrapped in curly braces ({}
) and others aren't?
Well, this is not a React syntax, this is more an ES6 Module syntax. Let's look at an example to better understand this point.
Go ahead to the mainLayout.jsx file and change const to default, and then go to the router.jsx file and remove the curly braces, and MAGIC! it works.
The curly braces are used to specify which components to import from a .jsx file. If you don't use the curly braces it will import the default component.
Now that we understand a little bit more of the import thing, let's understand what the mount() function is. The mount function syntax is like the following:
mount(ReactComponentToMountInTheScreen, parameters);
Now it make sense right? We are importing our layout component and just rendering it on the view, so simple! But what about the parameters? I'm talking about the header, content and footer, where are they, how can I import them, where do I put them, are we human or are we dancers? Wait... what?
So, let's create our three missing components here.
react/client/components/content.jsx
import React from 'react';
export default Content = ({name}) => (
<div>
<div className="section no-pad-bot" id="index-banner">
<div className="container">
<br/><br/>
<h1 className="header center orange-text">Hello, {name}</h1>
<div className="row center">
<h5 className="header col s12 light">A modern responsive front-end framework based on Material Design</h5>
</div>
<div className="row center">
<a href="http://materializecss.com/getting-started.html" id="download-button" className="btn-large waves-effect waves-light orange">Get Started</a>
</div>
<br/><br/>
</div>
</div>
<div className="container"/>
<div className="section">
<div className="row">
<div className="col s12 m4">
<div className="icon-block">
<h2 className="center light-blue-text">
<i className="material-icons">flash_on</i>
</h2>
<h5 className="center">Speeds up development</h5>
<p className="light">We did most of the heavy lifting for you to provide a default stylings that incorporate our custom components. Additionally, we refined animations and transitions to provide a smoother experience for developers.</p>
</div>
</div>
<div className="col s12 m4">
<div className="icon-block">
<h2 className="center light-blue-text">
<i className="material-icons">group</i>
</h2>
<h5 className="center">User Experience Focused</h5>
<p className="light">By utilizing elements and principles of Material Design, we were able to create a framework that incorporates components and animations that provide more feedback to users. Additionally, a single underlying responsive system across all platforms allow for a more unified user experience.</p>
</div>
</div>
<div className="col s12 m4">
<div className="icon-block">
<h2 className="center light-blue-text">
<i className="material-icons">settings</i>
</h2>
<h5 className="center">Easy to work with</h5>
<p className="light">We have provided detailed documentation as well as specific code examples to help new users get started. We are also always open to feedback and can answer any questions a user may have about Materialize.</p>
</div>
</div>
</div>
</div>
<br/><br/>
</div>
);
react/client/components/footer.jsx;
import React from 'react';
export default Footer = () => (
<footer className="page-footer orange">
<div className="container">
<div className="row">
<div className="col l6 s12">
<h5 className="white-text">Company Bio</h5>
<p className="grey-text text-lighten-4">We are a team of college students working on this project like it's our full time job. Any amount would help support and continue development on this project and is greatly appreciated.</p>
</div>
<div className="col l3 s12">
<h5 className="white-text">Settings</h5>
<ul>
<li>
<a className="white-text" href="#!">Link 1</a>
</li>
<li>
<a className="white-text" href="#!">Link 2</a>
</li>
<li>
<a className="white-text" href="#!">Link 3</a>
</li>
<li>
<a className="white-text" href="#!">Link 4</a>
</li>
</ul>
</div>
<div className="col l3 s12">
<h5 className="white-text">Connect</h5>
<ul>
<li>
<a className="white-text" href="#!">Link 1</a>
</li>
<li>
<a className="white-text" href="#!">Link 2</a>
</li>
<li>
<a className="white-text" href="#!">Link 3</a>
</li>
<li>
<a className="white-text" href="#!">Link 4</a>
</li>
</ul>
</div>
</div>
</div>
<div className="footer-copyright">
<div className="container">
Made by
<a className="orange-text text-lighten-3" href="http://materializecss.com">Materialize</a>
</div>
</div>
</footer>
);
react/client/components/navbar.jsx;
import React from 'react';
export default Navbar = () =>(
<nav className="light-blue lighten-1" role="navigation">
<div className="nav-wrapper container"><a id="logo-container" href="#" className="brand-logo">Logo</a>
<ul className="right hide-on-med-and-down">
<li><a href="#">Navbar Link</a></li>
</ul>
<ul id="nav-mobile" className="side-nav">
<li><a href="#">Navbar Link</a></li>
</ul>
<a href="#" data-activates="nav-mobile" className="button-collapse"><i className="material-icons">menu</i></a>
</div>
</nav>
);
And then let's use them in our router.jsx.
First you need to import them, so let's add the following code.
import Content from '/client/components/content.jsx';
import Navbar from '/client/components/navbar.jsx';
import Footer from '/client/components/footer.jsx';
and then just mount them, like this.
mount(MainLayout, {
header: <Navbar/>,
content: <Content/>,
footer: <Footer/>
});
So the full FlowRouter router.jsx file should look like this.
import React from 'react';
import {mount} from 'react-mounter';
import {MainLayout} from '/client/layouts/mainLayout.jsx';
import Content from '/client/components/content.jsx';
import Navbar from '/client/components/navbar.jsx';
import Footer from '/client/components/footer.jsx';
FlowRouter.route("/", {
action () {
mount(MainLayout, {
header: <Navbar/>,
content: <Content/>,
footer: <Footer/>
});
}
});
And then, the app should be looking like the initial image, a dead-simple Starter Template replica from the Materialize package.
Must Ask Questions
Why class
instead of className
?
Ben Alpert from the React core team explains the reasons pretty well on this quora topic
How can I access React's context of some component?
You should render the element inside the layout like a function instead of a simple react component.
For example:
<div className="container">
{content()}
</div>
And then the mount()
function should look like the following
mount(MainLayout, {
header: <Navbar/>,
content: () => (
<Content/>
),
footer: <Footer/>
});
How to pass parameters to a React component, i.e. Navbar or Content
Let's say we want to replace the Starter Template header on the <Content/>
component for a Welcome Message.
To achieve this, we need to make 3 changes to our existing code.
1. Make the Content Component accept parameters.
export default Content = ({name}) => ( )
2. Pass the parameters to our component, (similar to the blaze syntax but with just 1 curly brace).
<h1 className="header center orange-text">Hello, {name}</h1>
3. Pass the name value.
content: () => (
<Content name="Ethan"/>
),
And you should get something like this.
How do I Pass Reactive Data to My Components?
This is maybe the most important question of this whole tutorial, and sadly this will be covered in the next tutorial. If you don't want to wait until the next tutorial, you can check the following Medium post from @Arunoda
Summary
Today we learn the basics of React, we learn that Meteor works pretty well with React using the same ecosystem that of a normal npm app. What's more, we don't use anymore Meteor CLI to add react packages.
At this point, you should also be already following the Meteorhacks team on Github / Medium.
React is being accepted and loved by the JavaScript community, so don't think too much about learning React - it is the best option out there for the V in the MVC.
Hi, I’m trying to update the meteor version in this project, but it gets stuck extracting templating@1.1.9
I tried adding it manually but it says no version of templating satisfies all constraints:
*templating@1.1.9 <- top level
no idea what i can do… been stuck here for a while, and tried already some alternatives, but not finding any solution
great tutorial, when is the next part coming out?
meteor create --release 1.3-modules-beta.11 react
comes back unknown release?
instead … meteor npm install --save react react-dom ??