Fork and download the repo here: https://github.com/reactjs/react-tutorial

Coding an Interactive Comment Box with React JS!

A Newbie’s Guide to the Facebook Tutorial

9 min readSep 28, 2016

--

Sometimes coding newbies start learning about frameworks, tools, and technical concepts and get so intimidated by the tsunami of knowledge thrown their way that they start drowning. Wouldn’t we all love it if technical stuff wasn’t so technical all the time and was written to be more easily digestible? Here’s my stab at getting started with React (an open source library for building interactive user interfaces — a front end framework like Angular or Ember). This is based off Facebook’s own React getting started guide. Hope you have fun hacking away!

1. Start your server

  • It’s extremely important to get started by running your own local server. Using terminal, “cd” over to where you’re housing the repo you just downloaded. You can start the server a bunch of different ways using PHP, Python, Ruby, etc. Because I’m JavaScript centric, we’ll use node. Now type in the code below into your terminal (first line is unnecessary if you already have node installed).
npm install
node server.js
  • Congrats! Now you can head over to http://localhost:3000/ in your browser to see the local server you just created. It should have the comment box created from the example.js file provided. Just comment out the script tag in line 16 (see below). The server utilizes a JSON file provided by the tutorial — but you could customize this later on with any other JSON file.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>React Tutorial</title>
<!-- Not present in the tutorial. Just for basic styling. -->
<link rel="stylesheet" href="css/base.css" />
<script src="https://unpkg.com/react@15.3.0/dist/react.js"></script>
<script src="https://unpkg.com/react-dom@15.3.0/dist/react-dom.js"></script>
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>
<script src="https://unpkg.com/jquery@3.1.0/dist/jquery.min.js"></script>
<script src="https://unpkg.com/remarkable@1.6.2/dist/remarkable.min.js"></script>
</head>
<body>
<div id="content"></div>
<!--
<script type="text/babel" src="scripts/example.js"></script>
-->

<script type="text/babel">
var CommentBox = React.createClass({
render: function() {
return (
<
)
}
})
</script>
</body>
</html>

2. Start writing in the index.html

  • The tutorial suggests you write directly into the index.html file’s script tags — we’ll do that for simplicity sake, but you can simply just create your own file with a name like comments.js and do the same thing. Just toss it in the scripts folder and reference it in your html document like this :
<script type=”text/babel” src=”scripts/comments.js”>

</script>
The hierarchy suggested by Facebook to build out your comment box:

— Building a commentBox component

Now enter the code below to create your comment box <div>. Note that JSX used in the returned statement of the render function for simplicity sake. Although it looks like a DOM <div> element, this is actually a React <div> component that React knows how to handle (see in bold below). It is then eventually rendered to HTML by React. Pretty cool, huh?

var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
We created a React div component! WOO!
</div>

);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
//*Special Note*: React variables are capitalized (like a lot of other libraries / frameworks) while html elements start with lowercase letters

— Building a commentList and commentForm

Now that we’ve created the commentBox container which looks a lot like a DOM <div> to house our comments, let’s put in the commentList and commentForm components. Remember to put these variables ABOVE the commentBox variable declaration or it won’t render on the page.

var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Yeahhhh I am a CommentList.
</div>
);
}
});
var CommentForm = React.createClass({
render: function() {
return (
<div className="commentForm">
Party Parrot time. I am a CommentForm.
</div>
);
}
});

Now you want to keep the commentBox code we originally had and the render beneath it. In that commentBox code put in the <commentList /> and <commentForm /> components you declared. Your updated commentBox code should look like this:

var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>WOO My Comment Box</h1>
<CommentList />
<CommentForm />
</div>

);
}
});
ReactDOM.render(
<CommentBox />,
document.getElementById('content')
);
All our hard work thus far!

— Okay, had enough containers? Yup, we’re making more.

So comments are basically another <div> that you’ll render within commentList. Data is given to the comments variable which contain and author and children property. Then the data can be accessed using this.props.

  • Author: Whoever wrote the comment
  • Children: Any nested data (ie. things like responses to comments)
var Comment = React.createClass({
render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
{this.props.children}
</div>
);
}
});

Then we will enter comments into the commentList container like so:

var CommentList = React.createClass({
render: function() {
return (
<div className="commentList">
Yeahhhh I am a CommentList.
<Comment author="Kevin Kim">Nice, Comment Box</Comment>
<Comment author="Kevin Kim">Sometimes I talk to myself</Comment>

</div>
);
}
});
We’re getting real close now

— Adding markdown text

Markdowns are a simple way to format text inline. Facebook uses the Remarkable library. Basically it’s a library that takes text and converts it to raw HTML (that you can then directly execute). I found a nice demo of RemarkableJS here.

Booooo — ugly HTML tags not rendering

Now we can simply add a markdown element, which is a new Remarkable object. Then we want to render the children to raw HTML to display formatting. However, you’ll realize if you add the new markdown element, they’re not rendering properly on the screen. This is React protection method, which prevents XSS hacks (so hackers can’t input weird scripts that are executed to pwn you). So instead, we’ll have to settle for the quite difficult and “unsafe method” that creates a rawMarkup method in the comments object. We’ll go ahead and do it this way because we trust Remarkable, but it is important for us to remember steps like these are necessary (maybe not directly for this app), but in this day and age with so much personal data available on the web.

//Original way to add markups that React preventsvar Comment = React.createClass({
render: function() {
var md = new Remarkable();
return (
<div className="comment">
<h3 className="commentAuthor">
{this.props.author}
</h3>
{md.render(this.props.children.toString())}
</div>
);
}
});
//The workaround method - which we'll utilize because we trust Remarkable to be a safe resourcevar Comment = React.createClass({
rawMarkup: function() {
var md = new Remarkable();
var rawMarkup = md.render(this.props.children.toString());
return { __html: rawMarkup };
},

render: function() {
return (
<div className="comment">
<h2 className="commentAuthor">
{this.props.author}
</h2>
<span dangerouslySetInnerHTML={this.rawMarkup()} />
</div>
);
}
});
Nice, look at that sweet formatting!

3. Where my data at?

Now let’s start getting to the exciting stuff where we move away from our static placeholders in our HTML. We’re going to display the data dynamically!

The asterisk around *another* in the text of the data node italicized the text!

— Displaying the data dynamically

  • Now we want to source the data to our comment list by adding comment nodes. Then we can display them within the commentList <div>.
  • Then we want to make sure the commentList <div> is sourcing it’s data properly from the data object.
  • Finally, don’t forget to change how we render the page and make sure the CommentBox we’re rendering is properly linked (see all changes in bold below).
//Dummy data node you can use, courtesy of Facebook
var data = [
{id: 1, author: "Pete Hunt", text: "This is one comment"},
{id: 2, author: "Jordan Walke", text: "This is *another* comment"}
];
var CommentList = React.createClass({
render: function() {
var commentNodes = this.props.data.map(function(comment) {
return (
<Comment author={comment.author} key={comment.id}>
{comment.text}
</Comment>

);
});
return (
<div className="commentList">
{commentNodes}
</div>
);
}
});
var CommentBox = React.createClass({
render: function() {
return (
<div className="commentBox">
<h1>WOO My Comment Box</h1>
<CommentList data={this.props.data} />
<CommentForm />
</div>
);
}
});
ReactDOM.render(
<CommentBox data={data} />,
document.getElementById('content')
);
Suddenly your comments have changed!

— Linking to the provided server to become “Reactive”

  • Now we’ll be connecting to the API from our local server. If you’re unfamiliar with what an API is, it’s a communication channel that allows the client (whoever is requesting the data to display it — us in this example) to get data from the backend server (which lives in our comments.json file).
  • We’ll add a loadCommentsFromServer (an AJAX GET request to fetch data from the server), getInitialState (to return the initial data nodes) and componentDidMount method (to mount our JSON data to the commentBox that we received from the server).
  • Change the url source for the commentBox within the rendering of the commentBox and set a poll interval of every 2 seconds to refresh the data.
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);

},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm />
</div>
);
}
});
ReactDOM.render(
<CommentBox url="/api/comments" pollInterval={2000} />,
document.getElementById('content')
);

4. Interface to Let Users Add New Comments

Up to this point we’ve barely touched the comment form container. In this section is pretty much where all the $$$ of React lies. It’s the awesome interactiveness.

Below is the revamped code for the CommentForm and CommentBox. Basically, we’ve created inputs in the CommentForm object to take in user input. Then we’ve created some event handlers that will take in those submissions and renders the data inputted. The CommentBox is now also revamped so it nows how to handle submits and then POST that information to the local JSON file we have and then display the new updated state within the CommentBox.

var CommentForm = React.createClass({
getInitialState: function() {
return {author: '', text: ''};
},
handleAuthorChange: function(e) {
this.setState({author: e.target.value});
},
handleTextChange: function(e) {
this.setState({text: e.target.value});
},
handleSubmit: function(e) {
e.preventDefault();
var author = this.state.author.trim();
var text = this.state.text.trim();
if (!text || !author) {
return;
}
this.props.onCommentSubmit({author: author, text: text});
this.setState({author: '', text: ''});
},
render: function() {
return (
<form className="commentForm" onSubmit={this.handleSubmit}>
<input
type="text"
placeholder="Your name"
value={this.state.author}
onChange={this.handleAuthorChange}
/>
<input
type="text"
placeholder="Say something..."
value={this.state.text}
onChange={this.handleTextChange}
/>
<input type="submit" value="Post" />
</form>
);
}
});
var CommentBox = React.createClass({
loadCommentsFromServer: function() {
$.ajax({
url: this.props.url,
dataType: 'json',
cache: false,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
handleCommentSubmit: function(comment) {
var comments = this.state.data;
// Optimistically set an id on the new comment. It will be replaced by an
// id generated by the server. In a production application you would likely
// not use Date.now() for this and would have a more robust system in place.
comment.id = Date.now();
var newComments = comments.concat([comment]);
this.setState({data: newComments});
$.ajax({
url: this.props.url,
dataType: 'json',
type: 'POST',
data: comment,
success: function(data) {
this.setState({data: data});
}.bind(this),
error: function(xhr, status, err) {
this.setState({data: comments});
console.error(this.props.url, status, err.toString());
}.bind(this)
});
},
getInitialState: function() {
return {data: []};
},
componentDidMount: function() {
this.loadCommentsFromServer();
setInterval(this.loadCommentsFromServer, this.props.pollInterval);
},
render: function() {
return (
<div className="commentBox">
<h1>Comments</h1>
<CommentList data={this.state.data} />
<CommentForm onCommentSubmit={this.handleCommentSubmit} />
</div>
);
}
});

Now you should end up with this beautiful Comments Box! You did it, congrats for becoming a React master (well… not really).

Be sure to play around more and continue to optimize the comment box to your liking. Add CSS tags, play around with the JSON objects, add to your own website.

After adding comments, do you see it reflected in the comments.js file? It should — take a look at the file to the left:

React utilizes JQuery for AJAX requests and you can use it for and event handlers. You don’t need it though to use React. React also uses the Babel engine to translate ES6 syntax to ES5 because a lot of computers and browsers do not yet support ES6. https://babeljs.io/ — if you want a more complete understanding check out their site

Check out more cool things and all ReactJS documentation at: https://facebook.github.io/react/

--

--