Getting Started with Meteor 1.3 & React: Part 2
Hello Guys, like I promised, this is a continuation of the Meteor 1.3 with React Tutorial. If you guys missed it, here is the link:
What we are going to talk about
In this tutorial, we are going to look into deeper things about React, lifecycle hooks, managing meteor reactive data, and virtual DOM, JSX.
So in order to follow this tutorial, it will be great if you can take a look into the first part of this series.
Before we start, in this tutorial you will see sometime things like (in my opinion) or similar and this is mostly because what I'm saying today might change tomorrow, or it already changed.
Back to the basics
What's React?
The very first thing you need to ask yourself at this point, when you already get a little flavor about how the React syntax looks, how to create components, it should something like well.. I think I get react, its nice, it's from Facebook, everyone is talking about it... ok but WHAT IS IT?
Well, Do you remember that thing named MVC?
React is just the V inside MVC, so if you ever see some tutorial talking about comparing React vs Angular, Ember, Backbone, etc, follow these instructions.
- Go to the ChromeStore and download Block site
- Go to that tutorial page
- BLOOOOCK IT.
Now that you understand that React is just for the V A.K.A the view layer, you are okay to continue.
What's JSX?
JSX is a JavaScript syntax extension that looks similar to XML. You can use a simple JSX syntactic transform with React.
NOTE .jsx is not mandatory, but it's recommended to use.
In a nutshell, you will see more code-snippets and code out there for React in .jsx so you better get used to it, it's impossible to avoid it.
Virtual DOM
After having researched "what the hell is the React DOM", I found this: Tony Feed - Whats the React DOM. It's a great read and you should take a look.
In Meteor, you will see some people out there complaining (or not complaining) about changing from Blaze to React because the re-rendering stuff and they way Blaze vs React manages the way you re-render your DOM.
And they are (in my opinion) Wrong, React and Blaze work mostly the same way--they both update your DOM where is needed.
Lifecycles
So, React components have their own methods better called "lifecycles hooks". Here is a list of the most important ones, in logical order.
Component Specifications
- render The
render()
method is required.
When called, it should examine this.props
and this.state
and return a single child element. This child element can be either a virtual representation of a native DOM component (such as <div />
or React.DOM.div()
) or another composite component that you've defined yourself.
-
getInitialState - Invoked once before the component is mounted. The return value will be used as the initial value of
this.state
. -
getDefaultProps - Invoked once and cached when the class is created. Values in the mapping will be set on
this.props
if that prop is not specified by the parent component (i.e. using an in check).
This method is invoked before any instances are created and thus cannot rely on this.props
. In addition, be aware that any complex objects returned by getDefaultProps()
will be shared across instances, not copied.
-
Mounting
-
ComponentWillMount Invoked once, both on the client and server, immediately before the initial rendering occurs. If you call
setState
within this method,render()
will see the updated state and will be executed only once despite the state change. -
ComponentDidMount Invoked once, only on the client (not on the server), immediately after the initial rendering occurs. At this point in the lifecycle, you can access any refs to your children (e.g., to access the underlying DOM representation). The
componentDidMount()
method of child components is invoked before that of parent components.
Unmounting
- ComponentWillUnmount Invoked immediately before a component is unmounted from the DOM.
Perform any necessary cleanup in this method, such as invalidating timers or cleaning up any DOM elements that were created in componentDidMount
.
Updating
-
componentWillReceiveProps(nextProps) Invoked when a component is receiving new props. This method is not called for the initial render.
-
shouldComponentUpdate(nextProps, nextState) Invoked before rendering when new props or state are being received. This method is not called for the initial render or when
forceUpdate
is used. -
componentWillUpdate(nextProps, nextState) Invoked immediately before rendering when new props or state are being received. This method is not called for the initial render.
Use this as an opportunity to perform preparation before an update occurs!
Getting Started with Meteor & React
Now that you've gotten things in order, at this point you have already a notion of what's React, what's NOT React, and how to make it work with your favorite framework like Meteor =D (because it is your favorite).
You might been wondering, well and now how can I start powering my React apps? Passing real data from my backend, how is this possible?
And the answer to your question is.
npm install --save --save-exact react-mounter # our savor
So again, our friends from kadirahq, do the dirty job for us and give us a handy solution to handle our data, with a useful partner called the "React Container Pattern".
I will recommend you to take a look into the following post on medium from @arunoda:
Let's compose some React containers.
To get an idea of what we are talking about.
FAQ
- Which one should I use?
As far I know with Meteor 1.3 there are 2 ways to handle the data with Meteor.
react-meteor-data
Repo - TODO React
And
react-komposer
Repo - Meteor Data And React 1.3
In this minimal example, I will be using the react-komposer library.
- Where I can find more info about this discussion?
Go ahead and follow this issue:
What's next for React + Meteor 1.3
Okay okay, let's REALLY get started
All the code used here is inside this repo.
What we are going to create?
As you can see, we are going to create a simple grid with a post inside it, and we are going to use the Cards component from materialize for this.
Go ahead to the /lib folder and create the /lib/collections/posts.js file, then create your posts collection.
Posts = new Mongo.Collection('posts');
Then go to the server and create a file named server/seed/posts.js, with the following content.
if(_.isEqual(Posts.find().count(),0)){
for(let i = 0; i < 25; i++){
Posts.insert({
name: "Post Test " + [i] + 1,
subbmitedAtt: new Date()
});
};
};
What we are doing here is simple: we are creating a Collection Post, and inserting 25 posts (only when the collection is empty), just for testing purposes.
Still on the server, go and create the publications file, name it server/publications/posts.js, and put the following content.
Meteor.publish("posts", () =>{
return Posts.find();
});
NOTE: If you dont know anything about publications, check out the official docs.
At this point, we have the basics to start playing with data, we have:
- A Collection named Post
- Seed Data
- Publication Method.
Now is the time to start playing with our components and containers.
Let's create our PostCard component
In our /components folder, create the postLists
file and name it like this client/components/postsList.jsx, with the following content.
import React from 'react';
const CardItem = ({name}) =>(
<div className="col s6 m2">
<div className="card blue">
<div className="card-content white-text">
<span className="card-title">{name}</span>
<p></p>
</div>
</div>
</div>
);
const PostList = ({posts}) =>(
<div className="row">
{posts.map(({name}) => (
<CardItem name={name} />
))}
</div>
);
export default PostList;
As you can see, these look like simple React components. First we are creating the CardItem
component, and then in our PostList
component we are calling it.
Now you might be wondering: Where are we getting the {posts}
parameters, and why we are mapping it? Where is that array coming from?
Well, here is where our container comes to action.
Go ahead and create the following file client/containers/postsList.js
NOTE: Do you remember that I told you that React is just the V on MVC? Our container will be the C in the MVC - our controller (yes our containers are our controllers. People like to reinvent the wheel so much these days). That's why our container can be plain .js instead of .jsx.
Now put this code in client/containers/postsList.js:
import {composeWithTracker} from 'react-komposer';
import PostList from '/client/components/postsList.jsx';
function composer(props, onData) {
const handle = Meteor.subscribe("posts");
if (handle.ready()) {
const posts = Posts.find().fetch();
onData(null, {posts});
};
};
export default composeWithTracker(composer)(PostList);
The first two lines imports the composeWithTracker
method from the react-komposer library, and our PostList
component.
Then, we are creating the composer
function, which accepts two parameters {props, onData}
, and in the body of this function, you should be maybe familiar with what we are doing.
We are subscribing to our publication posts, then we are waiting it to be ready (yes, composeWithTracker
is Reactive).
Then, we are passing to the onData(callback)
, the query with all the posts.
And finally, our component is ready to be "composed" and exported!
Read more about react-mounter in the README.md
Easy right? There is still one last step pending. Let's create the route for the Post List.
Go to our client/routing/routing.jsx file, and add these lines of code.
First, import the container - NOT the component.
import PostList from '/client/containers/postsList.js';
Then, create the route.
FlowRouter.route("/routes", {
action () {
mount(MainLayout, {
header: <Navbar />,
content: <PostList />,
footer: <Footer />
});
}
});
This route is similar to the home route, just that we are passing <PostList />
as content.
Checking Meteor Magic Reactivity
Go and type
meteor shell
You can open another terminal pressing Command + T in the console
and then insert it.
Posts.insert({name:"Post Test 25"})
Your terminal should look like this:
And your app should have the post 25 already there!
Recommendations
At this point, you should be already well-versed with the basics of React, but you should know more about react. It's worth researching the WWW to learn more, and here's some advice from me.
React is good and all, but don't use it only because it's getting all the "hype", try to check if is worth it for you to move from Blaze to React.
If React fits your needs, and you like it, go ahead and use it. Right now on the www there is some hate about React, i.e.
Stop Using React for EVERYTHING!
Panda Strike: React Is a Terrible Idea
So Ethan, what's the deal with giving us 2 links that is literally telling us to don't use what we just learned?
Well maybe I'm crazy, but every time I want to start learning something, I go and check the bad things about it, and then I'll decide if they are really bad things. It's what I did when I started learning Meteor =p
And that's all for now, I'm going to be working this days /weeks in making a bigger app and that integrates React + Meteor 1.3 + GraphQL, so stay tuned!
Thanks. This will be handy for porting Meteor Candy to React.
import PostList from ‘/client/components/postsList.jsx’;
needs to be
import PostList from ‘/client/components/postList.jsx’;
Thanks for the continued demo, I’m learning a lot!
I am however getting an error with your example that it not obvious for me to fix. I installed react-komposer with npm but I’m getting a warning: React.createElement: type should be null… message. Any clues? I’ve just cut/pasted your code following along. Seems to be related to the PostList component.
Durp… found it. The export statement in the postLists.jsx content above was below the scroll line. That fixed it.