MernCommentBox
A Tutorial on creating a MERN Stack App from the Facebook Comment React Tutorial
Install / Use
/learn @bryantheastronaut/MernCommentBoxREADME
React Getting Started -- The MERN Stack Tutorial! (feat. ES6!)
Hello!
In this tutorial, I will attempt to show you how to create a basic MERN (MongoDB/Express/React/Node.js) Stack application. We will be using Facebook's React Tutorial which can be found here.
What this guide expects:
- Basic understanding of React (the above tutorial is a great place to start)
- ES6 Javascript -- I will try to make note when using something from it, but this guide is not a tutorial on either React or ES6.
- Node/NPM installed
What this guide provides:
- Introduction to setting up a full stack application with the MERN architecture.
- Setting up a CRUD (create, read, update, delete) API via Express
- Using that API to interact with an external database (we will use the free tier of MLab, but setting up your own is a small step from there.)
- Terminal commands are from OSX -- if you are using Windows, commands may vary slightly
We will be using Facebook's new Create React App to get started, so if you don't have that available, go into your terminal and type in npm i -g create-react-app (i is short for install, -g does a global install to be able to call it right from the command line. You may need to prefix this command with sudo if you do not have root access).
Okay, so lets get started!
The GitHub repo for this tutorial is available here.
Our first steps...
The first thing we will do is create a skeleton React application to work with. This will save us from needing to configure a webpack file, incorporate babel for JSX use, and other React setup headaches.
create-react-app merncommentbox && cd merncommentbox will create the base for us and bring us to our working directory.
if we run npm start, we get this pretty little app here: 
Let's do a bit of cleanup of unnecessary files to start.
cd src && rm App.css App.js App.test.js favicon.ico index.css logo.svg to get rid of everything in the src file besides the index.js file.
Now head into the index.html in your root folder and remove the <link rel="shortcut icon" href="./src/favicon.ico"> line in the head, since that file no longer exists. While we are here, we can change the title to something a bit more proper than the generic 'React App' -- I will call mine "Bryan's Fancy Full Stack App".
Now we can head into the index.js file to get rid of the files we removed. Based on Facebook's Tutorial, we can make a change to the ReactDOM call as well and put in what we will be making next, which is the CommentBox. Your index.js file should look like this:
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import CommentBox from './CommentBox';
ReactDOM.render(
<CommentBox />,
document.getElementById('root')
);
We're so close to rendering our first thing! Let's touch src/CommentBox.js src/Comment.js src/CommentForm.js src/CommentList.js to create all the components we will be working with. Let's also touch src/data.js to make sure everything is working before we dive in. We should also make a folder to hold the Schema for our database, so we should make a folder called model with a comments.js file inside.
Our file structure will look like this:
- model
- comments.js
- node_modules
- ...various modules...
- src
- Comment.js
- CommentBox.js
- CommentForm.js
- CommentList.js
- data.js
- index.js
- style.js
- .gitignore
- index.html
- package.json
- README.md
- server.js
I am a fan of modular inline css styling, so also touch src/style.js if you want to follow along with that. If you prefer to use a style.css file instead, feel free. Please note that if you do, you will have to replace every
import style from './style';
call I use with
import './style.css';
and every
style={ style.uniqueName }
with
className='uniqueName'
Go ahead and copy this file into style.js, and feel free to make any changes you see fit. It's not the prettiest, but its better than nothing.
//style.js
const style = {
commentBox: {
width:'80vw',
margin:'0 auto',
fontFamily:'Helvetica, sans-serif'
},
title: {
textAlign:'center',
textTransform:'uppercase'
},
commentList: {
border:'1px solid #f1f1f1',
padding:'0 12px',
maxHeight:'70vh',
overflow:'scroll'
},
comment: {
backgroundColor:'#fafafa',
margin:'10px',
padding:'3px 10px',
fontSize:'.85rem'
},
commentForm: {
margin:'10px',
display:'flex',
flexFlow:'row wrap',
justifyContent:'space-between'
},
commentFormAuthor: {
minWidth:'150px',
margin:'3px',
padding:'0 10px',
borderRadius:'3px',
height:'40px',
flex:'2'
},
commentFormText: {
flex:'4',
minWidth:'400px',
margin:'3px',
padding:'0 10px',
height:'40px',
borderRadius:'3px'
},
commentFormPost: {
minWidth:'75px',
flex:'1',
height:'40px',
margin:'5px 3px',
fontSize:'1rem',
backgroundColor:'#A3CDFD',
borderRadius:'3px',
color:'#fff',
textTransform:'uppercase',
letterSpacing:'.055rem',
border:'none'
},
updateLink: {
textDecoration:'none',
paddingRight:'15px',
fontSize:'.7rem'
},
deleteLink: {
textDecoration:'none',
paddingRight:'15px',
fontSize:'.7rem',
color:'red'
}
}
module.exports = style;
While we are here, let's install some other dependencies we will need coming up. npm i axios express body-parser foreman nodemon marked mongoose --save which will give us:
-
Axios: let us use HTTP methods to communicate with our database.
-
Express: provide a framework to set up our Node.js server.
-
Body Parser: parse the incoming requests bodies for the info we are sending (gives us access to
req.bodywhich we will be using soon). -
Foreman: allows us to boot up our API and webpack-dev-server simultaneously.
-
Nodemon: watches our server.js file for changes, then restarts it.
-
Marked: to convert markdown syntax to html. I used this in place of Remarkable (which the FB tutorial uses)
-
Mongoose: abstracts away the MongoDB boilerplate for a simple Schema solution.
Okay! We're all set up! Since this is not a React tutorial, I am just going to dump each component structure below as it is statically, then we will move forward with creating the server and rendering everything dynamically!
The Components!
CommentBox.js
//CommentBox.js
import React, { Component } from 'react';
import CommentList from './CommentList';
import CommentForm from './CommentForm';
import DATA from '../data';
import style from './style';
class CommentBox extends Component {
constructor(props) {
super(props);
this.state = { data: [] };
}
render() {
return (
<div style={ style.commentBox }>
<h2>Comments:</h2>
<CommentList data={ DATA }/>
<CommentForm />
</div>
)
}
}
export default CommentBox;
CommentList.js
//CommentList.js
import React, { Component } from 'react';
import Comment from './Comment';
import style from './style';
class CommentList extends Component {
render() {
let commentNodes = this.props.data.map(comment => {
return (
<Comment author={ comment.author } key={ comment.id }>
{ comment.text}
</Comment>
)
})
return (
<div style={ style.commentList }>
{ commentNodes }
</div>
)
}
}
export default CommentList;
CommentForm.js
//CommentForm.js
import React, { Component } from 'react';
import style from './style';
class CommentForm extends Component {
constructor(props) {
super(props);
this.state = { author: '', text: '' };
this.handleAuthorChange = this.handleAuthorChange.bind(this);
this.handleTextChange = this.handleTextChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleAuthorChange(e) {
this.setState({ author: e.target.value });
}
handleTextChange(e) {
this.setState({ text: e.target.value });
}
handleSubmit(e) {
e.preventDefault();
console.log(`${this.state.author} said "${this.state.text}"`)
//we will be tying this into the POST method in a bit
}
render() {
return (
<form style={ style.commentForm } onSubmit={ this.handleSubmit }>
<input
type='text'
placeholder='Your name...'
style={ style.commentFormAuthor}
value={ this.state.author }
onChange={ this.handleAuthorChange } />
<input
type='text'
placeholder='Say something...'
style={ style.commentFormText}
value={ this.state.text }
onChange={ this.handleTextChange } />
<input
type='submit'
style={ style.commentFormPost }
value='Post' />
</form>
)
}
}
export default CommentForm;
Comment.js
//Comment.js
import React, { Component } from 'react';
import style from './style';
import marked from 'marked';
class Comment extends Component {
rawMarkup() {
let rawMarkup = marked(this.props.children.toString());
return { __html: rawMarkup };
}
render() {
return (
<div style={ style.comment }>
<h3>{this.props.author}</h3>
<span dangerouslySetInnerHTML={ this.rawMarkup() } />
</div>
)
}
}
export default Comment;
data.js
//data.js
const data = [
{ id: 1, author: 'Bryan', text: 'Wow this is neat!'},
{ id: 2, author: 'You', text: 'youre __right!__'}
]
module.exports = data;
Okay now let's get to it for real!
Setting up server.js
From here we go
