Part 1: Rewrite Facebook Tutorial using Babel 6, ES6 and Webpack

This is the first part of the series, you can also check the Part 2: Server Rendering a ES6 React.js App Using Ruby on Rails

Facebook have a very good tutorial on how to use React.js to build a comment system which you can post comment to the server, live update the comment list and retrieve comments from server using Ajax.

In this serials, we will first modify the tutorial a little bit, rewrite the sample use ES6, compile use Babel and Webpack in part 1, and then in part 2 we will pre-render the content in a Ruby on Rails app, and add caching to speed up the performance.

Prerequisites

You need to understand React.js a little bit, and went through Facebook official React.js tutorial first. This tutorial is on top of the original tutorial.

To get started, you need to make sure you have installed same node version. Using different versions might have different results which are hard to debug.

Install nvm

Install nvm for node version manager. nvm is a tool similar to rvm, which helps to easily install multiple versions of node.

$ brew install nvm

Install node version 5.3.0

As the time of writing, version 5.3.0 is the latest stable node version available in nvm, you can check all node versions using nvm ls-remote.

$ nvm install 5.3.0

make sure your node version is correct by run

$ node -v

You should see correct

If your node version is something different, you might need to remove node or change path priority. brew uninstall node.

Getting Started

1. Create project folder and package.json

Create a new folder for the demo, cd to the folder and run

$ npm init

You can leave everything as default for now.

2. Install dependencies

Install Webpack

$ npm install webpack webpack-dev-server --save-dev

Install babel

$ npm install babel-core babel-preset-react babel-preset-es2015 babel-loader --save-dev

Install eslint

$ npm install eslint eslint-config-airbnb --save-dev

Install React and other libraries used in the project

$ npm install react react-dom marked --save

3. Add configs

1. Add .nvmrc

It is good to make sure other people who came to this project also use the same node version as you. You can add a new file to the root.

5.3.0

New comer will just need to run nvm use to switch to the version the project needs.

2. Add .babelrc

{
  "presets": ["es2015", "react"]
}

3. Add .eslintrc.json

{
  "extends": "airbnb",
  "plugins": [
    "react"
  ]
}

4. Add .eslintignore

node_modules/**

5. Add webpack.config.js

module.exports = {
  entry: './App.js',
  output: {
    filename: 'bundle.js',
  },
  module: {
    loaders: [
      { test: /\.jsx?$/, loader: 'babel', exclude: /node_modules/ },
    ],
  },
  resolve: {
    extensions: ['', '.jsx', '.js'],
  },
};

4. Add React files

Comment.js

import React, { Component, PropTypes } from 'react';
import marked from 'marked';

class Comment extends Component {
  rawMarkup() {
    return {
      __html: marked(this.props.children.toString(), { sanitize: true }),
    };
  }

  render() {
    return (
      <div className="comment">
        <h2 className="commentAuthor">
          {this.props.author}
        </h2>
        <span dangerouslySetInnerHTML={this.rawMarkup()} />
      </div>
    );
  }
}

Comment.propTypes = {
  children: PropTypes.node.isRequired,
  author: PropTypes.string.isRequired,
};

export default Comment;

CommentList.js

import React from 'react';
import Comment from './Comment';

export default ({ data }) => (
  <div className="commentList">
    {data.map(comment =>
      <Comment author={comment.author} key={comment.id}>
        {comment.text}
      </Comment>
    )}
  </div>
);

CommentForm.js

import React, { Component, PropTypes } from 'react';

class CommentForm extends Component {
  constructor(props) {
    super(props);
    this.state = { author: '', text: '' };
  }

  handleAuthorChange(e) {
    this.setState({ author: e.target.value });
  }

  handleTextChange(e) {
    this.setState({ text: e.target.value });
  }

  handleSubmit(e) {
    e.preventDefault();
    const author = this.state.author.trim();
    const text = this.state.text.trim();
    if (!text || !author) {
      return;
    }
    this.props.onCommentSubmit({ author, text });
    this.setState({ author: '', text: '' });
  }

  render() {
    return (
      <form className="commentForm" onSubmit={(e) => this.handleSubmit(e)}>
        <input
          type="text"
          placeholder="Your name"
          value={this.state.author}
          onChange={(e) => this.handleAuthorChange(e)}
        />
        <input
          type="text"
          placeholder="Say something..."
          value={this.state.text}
          onChange={(e) => this.handleTextChange(e)}
        />
        <input type="submit" value="Post" />
      </form>
    );
  }
}

CommentForm.propTypes = {
  onCommentSubmit: PropTypes.func.isRequired,
};

export default CommentForm;

CommentBox.js

import React, { Component, PropTypes } from 'react';
import CommentList from './CommentList';
import CommentForm from './CommentForm';

class CommentBox extends Component {
  constructor(props) {
    super(props);
    this.state = { data: props.data || [] };
  }

  handleCommentSubmit(comment) {
    const comments = this.state.data;
    const newComments = comments.concat([comment]);
    comment.id = Date.now();
    this.setState({ data: newComments });
    // TODO: send to API
  }

  render() {
    return (
      <div className="commentBox">
        <h1>Comments</h1>
        <CommentList data={this.state.data} />
        <CommentForm onCommentSubmit={(comment) => this.handleCommentSubmit(comment)} />
      </div>
    );
  }
}

CommentBox.propTypes = {
  data: PropTypes.array,
};

export default CommentBox;

5. Add index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>React Tutorial</title>
  </head>
  <body>
    <div id="content"></div>
    <script src="bundle.js"></script>
  </body>
</html>

6. Update package.json scripts

"scripts": {
  "eslint": "./node_modules/.bin/eslint .; exit 0",
  "start": "./node_modules/.bin/webpack-dev-server"
},

You can run npm run eslint to check your code syntax using Airbnb Javascript best practice.


You might see errors above which helps you to fix any potential Javascript errors.

7. Run in localhost

Now run

$ npm start

You should see something like this in your terminal

Now open your browser and go to localhost:8080 and you should see something like this

Try to post a new comment, it should update the comments automatically. If everything is working fine, we can move to Part 2: Server Rendering a ES6 React.js App Using Ruby on Rails.

You can check the full source codes in Github.

Other resources:

View Comments