This is the first part in a series of articles, where we’ll be using React to build a Slack-style chat application.
In this article, we’ll start by building a basic prototype. We’ll start by learning about React components and JSX. While doing that, we’ll also look at the basics of building React applications.
React terminology
Before we jump into code, let’s quickly look at some React terms. This will make it easier to understand what’s going on.
- Babel: A JavaScript compiler. Babel can read code, and compile it into code that the browser understands, in other words, it compiles code into JavaScript.
- JSX: Most React code uses a language called JSX. It’s essentially JavaScript, but it allows embedding XML into it. We use Babel to compile it into something the browser understands.
- Component: React applications are built out of components. A component is just a JavaScript object which has certain functions.
With that out of the way, let’s get started.
Getting started
We’re going to start simple. That means we’re just going to create an HTML page, and put our JavaScript code there. In a later article, we’ll be looking at a different way of doing it, but for now, let’s just focus on React itself.
Here’s the HTML for the page:
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script> </head> <body> <div id="app"></div> <script type="text/babel"> // We'll put our code here. </script> </body> </html> |
In this page, we’re loading three script files: react.js
, react-dom.js
and browser.js
.
react.js
loads theReact
module, which contains most of the React libraryreact-dom.js
loadsReactDOM
. This makes it possible to render React components into HTMLbrowser.js
loads the Babel compiler, which lets us use React’s JSX language
Inside the body tag, we’ve got a div and a script tag. We’ll render our React application inside the div. In the spirit of keeping things simple, we’ll be putting our code into the script tag.
Note that the script tag’s type
attribute is set to text/babel
. This tells the Babel compiler to parse any code and compile it. Without this, we cannot use JSX.
Building a simple component
Now that we’ve got all the libraries loaded and the page set up, let’s build a simple component to show something.
var Chat = React.createClass({ render: function() { return <div>Hello</div>; } }); |
Here, we create a component called Chat
. The function React.createClass
is a helper for building React components. We pass it an object containing the properties for the component. In this case, we only include a render
function.
You’ll notice we have some HTML within render
. This is possible because we’re using JSX. It might not seem very useful at this point, but being able to use HTML inside JavaScript code makes more complex components easier to write.
The purpose of the render function is to define how the component should be rendered. In this case, we output a div
with the text “Hello” inside it.
In order to make this show up on the page, we need to render it. For this, we’ll use ReactDOM
:
ReactDOM.render( <Chat />, document.getElementById('app') ); |
In addition to HTML elements, JSX allows using components. Note that we’re using the self-closing tag markup <Chat />
here – JSX requires the markup to be valid XML. When the element has no content, we need to close it like above.
The second parameter is the element where we want the rendering output – in this case, the app div.
If you put the code into the script tag and then load the page, you’ll see the message Hello show up.
Below is the full code for the page. You can also see it in this JSFiddle.
<!DOCTYPE html> <html> <head> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.6/react-dom.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.8.23/browser.js"></script> </head> <body> <div id="app"></div> <script type="text/babel"> var Chat = React.createClass({ render: function() { return <div>Hello</div>; } }); ReactDOM.render( <Chat />, document.getElementById('app') ); </script> </body> </html> |
The idea with React is that any markup you build should be done inside React components. If you’ve used AngularJS, the idea is similar to doing any DOM manipulation within directives.
The JSX XML syntax is compiled into normal JavaScript code by Babel. Any piece of code which has XML is converted into React.createElement
. If you want to see what that looks like, you can play around with Babel REPL.
Displaying a simple chat UI
Displaying a greeting is great and all, but since we’ll build a chat application, let’s maybe display something more relevant.
With JSX, it’s very easy to change how our component is rendered. We’ll simply put different HTML into the render function!
var Chat = React.createClass({ render: function() { return <div> <div>Messages should be displayed here</div> <form> <input type="text" placeholder="Your message" /> <input type="submit" value="Send" /> </form> </div>; } }); |
That’s more like it. If you update the Chat component to look like that, you’ll see a div for chat messages, and a form for sending some.
To make the form do something, we need some event handlers. Let’s start by adding a submit handler:
var Chat = React.createClass({ submit: function(ev) { ev.preventDefault(); }, render: function() { return <div> <div>Messages should be displayed here</div> <form onSubmit={this.submit}> <input type="text" placeholder="Your message" /> <input type="submit" value="Send" /> </form> </div>; } }); |
We’ve added a new function: submit
. We can add our own functions into components when needed, in this case, we’ll use this to handle sending a new chat message. For now, the function only prevents the default event handling – otherwise, submitting the form would reload the page.
In the render function, we’ve added onSubmit={this.submit}
into the form tag. When using JSX, attributes like onsubmit
should be camelCased, so we use onSubmit
instead.
Whenever we want to include a value from JavaScript within our JSX markup, we need to wrap it within curly braces { }
. In this case, we use this syntax to assign the submit
function as the onSubmit
handler. We can also use curly braces to display data, as you’ll see a bit later.
An important thing to note here is that using onSubmit={this.submit}
is different from using inline event handlers within HTML. In HTML, inline handlers are discouraged, but with React, using them is the norm. They are handled differently behind the scenes.
Tracking state
To save a new chat message, we need to know what the user typed into the input box.
Let’s add another event handler to track the input box’s value:
var Chat = React.createClass({ submit: function(ev) { ev.preventDefault(); }, updateInput: function(ev) { var text = ev.target.value; }, render: function() { return <div> <div>Messages should be displayed here</div> <form onSubmit={this.submit}> <input onChange={this.updateInput} type="text" placeholder="Your message" /> <input type="submit" value="Send" /> </form> </div>; } }); |
Now we have the text, but where do we save it to be able to access it from submit
?
The answer is using state. Each React component can optionally keep track of the state of some values. First, we need to define the initial state of the component. After that, we can use setState
to update it.
var Chat = React.createClass({ getInitialState: function() { return { text: '' }; }, updateInput: function(ev) { this.setState({ text: ev.target.value }); }, /* other code omitted */ }); |
The getInitialState
function should return an object containing the default state for your component. In this case, we want the text be empty by default. Then, in the update handler function, we call this.setState
with an object containing the values we want to update.
Now that we have a place to save the text, we can use it in our submit function. However, in addition to the text, we need a way to store the chat messages. Let’s change the state slightly, so we can also save messages into it.
var Chat = React.createClass({ getInitialState: function() { return { text: '', messages: [] }; }, submit: function(ev) { ev.preventDefault(); var newMessage = this.state.text; this.setState({ messages: this.state.messages.concat([newMessage]) }); }, /* other code omitted */ }); |
We can read values from the component’s state using this.state
. Here, we grab the input box’s value and save it as a new message. But why are we using this weird looking concat
thing?
You should avoid changing values in this.state
and only do it through setState
. By using concat, we can append the new message into the array without changing the original array. The reason for using setState
is that React uses this to check whether or not it needs to re-render anything. If we update this.state
directly, then React can’t do that.
The only thing left now is displaying the messages. We can use the curly brace syntax again in the render function to show them:
var Chat = React.createClass({ /* code omitted */ render: function() { return <div> <div>{this.state.messages}</div> <form onSubmit={this.submit}> <input onChange={this.updateInput} type="text" placeholder="Your message" /> <input type="submit" value="Send" /> </form> </div>; } }); |
By replacing the contents of the message div with {this.state.messages}
, we tell React to render the contents of the variable into the markup.
You can see the full code in this JSFiddle.
Displaying the messages in a nicer way
If you tried the code we have so far, you might’ve noticed that the messages are displayed with no line breaks after them. Let’s fix that.
Let’s create a second component which handles rendering a chat message. We’ll add the following code into the script tag:
var ChatMessage = React.createClass({ render: function() { return <p>{this.props.message}</p>; } }); |
We only have a render function in this component, but what’s this.props.message
?
In addition to state, React components can have properties – or props. While you can change state, you should never change props.
The idea is we can simplify our code by having simple components which contain no state – only props. In addition, we can have some components with state, but we should think carefully where we need that. In this case, since the purpose of ChatMessage
is to only display the message, we don’t need to keep any state in it.
We can update the submit function to save a ChatMessage
:
submit: function(ev) { ev.preventDefault(); var newMessage = <ChatMessage message={this.state.text} />; this.setState({ messages: this.state.messages.concat([newMessage]) }); }, |
The only thing we changed is we assign a ChatMessage
. Notice we set the message attribute’s value to this.state.text
– which sets the value for this.props.message
inside the ChatMessage
. In other words, the JSX attributes for a component get copied into props.
You can see the full code in action in this JSFiddle.
This fixes the problem and each message is now displayed on its own line. Note that we didn’t change the render function for Chat
– React is smart enough to figure out when we have an array of components, and it renders them as you would expect.
What’s next?
Now that we have a basic prototype, what’s next? The next thing to do would be to clear the input box when we send a message. But I’ll leave that as an exercise for you.
After that, there’s plenty more.
We’ll need to add some nicer styling, add support for actually having multiple people chatting together (right now this isn’t really a chat at all…)… We probably want to have some unit tests too. In addition, we’ll also be looking at using browserify and ES6 features in future articles.
Next, we’ll set up some tools that are going to make our lives a lot easier in the long run. Here’s the next article in the series.
If you liked this, you should sign up using the form below so I can send you new follow-up articles when they are ready.