Building a chat component in React and ES6

This is the third part of the React-Redux series.

In the first part, we introduced React and Redux. Although there was no code in the article, clarifying the concepts is still important.

In the second part, we set up the development environment with webpack, npm, and live reload. This setup will be extended later with automated testing, we did just enough to get started with writing applications.

We are still laying down the foundations. The goal of part 3 is to understand how we write components in React. We will continue using the ES6 syntax. If you are interested in getting up to speed with ES6, check out my course ES6 in Practice.

Later we will change both the boilerplate and our approach after introducing Redux. However, it is very important to understand the foundations in advance.

Revealing the component

You can click the below CodePen to reveal the application we will build today. Feel free to try out the application, but don't worry about the code yet. I will explain everything in depth.

See the Pen yVxZQW by Zsolt (@zsolt555) on CodePen.

As you enter your name and your message, they appear in a chat window. You can add multiple messages. By clicking the Clear button, all messages disappear. The Clear button is enabled whenever there is at least one displayed message.

We will use React and ES6 to build this application. Let's get started with some theory.

Everything is a component

In React, everything is a component. Components may contain components. As soon as a component becomes complex, you can break it down to subcomponents that communicate with each other.

A component is responsible for rendering its HTML code. When calling the render method of your component, its HTML code is generated.

We will later learn that components can either be stateful or stateless. Stateless components are normally included in stateful components. Stateful components are automatically re-rendered by React, whenever their state changes.

Re-rendering components does not necessarily imply DOM updates. React components are re-rendered using an in-memory virtual DOM. The real DOM is only updated when there is a change in the virtual DOM. This process is called diffing. Only differences are re-rendered in the real DOM, which results in a performance boost in most of the cases.

A simple "Hello, World!" component looks like this:

In order to write a React component, we have to extend the React.Component class. Components always have a render function that return the React representation of the rendered node. A syntactic sugar for this representation is JSX.

JSX allows us to describe HTML markup with minimal clutter. JSX is translated into React method calls by the Babel JSX transpiler. Check out the setup of a React application for more information.

For the sake of simplicity, treat JSX as if you wrote HTML code. There are minor differences between HTML syntax and JSX syntax. For instance, given that the class keyword is reserved for JavaScript, you have to use className instead of class to describe the class attribute of a tag.

The syntax {this.props.message} inserts the value of the message attribute defined for SimpleComponent. We will soon define this attribute when rendering the component.

If the extends keyword or the concise method syntax of render is not familiar to you, sign up for my ES6 course at the bottom of this article to learn and understand the syntax.

In order to render a component to a webpage, we will use the ReactDOM object.

The arguments of ReactDOM.render are as follows:

  • the component markup in JSX, invoking the render method of the component,
  • a container where the markup is rendered.

JSX introduces custom tags for our components with the name of the component. By writing these custom tags inside the render method of a component, we can nest components inside each other.

The message attribute will become a prop of SimpleComponent. Props are accessed using the this.props object inside the component.

Clone or download my React GitHub repository to see the code in action.

JSX in depth

Let's summarize what we already know about JSX:

  • JSX is syntactic sugar to define the HTML markup of components, possibly containing other components
  • JSX is transpiled to React method calls
  • we may evaluate JavaScript expressions inside { and } and include them in the markup
  • both HTML markup and React components may have attributes. These attributes work in the same way as HTML attributes. Some exceptions apply, for instance, instead of the class attribute, you have to use className.

This knowledge was enough to get started with JSX. We will now expand your knowledge about JSX.

Recall our code example containing two JSX expressions:

HTML markup is defined with tags that are not capitalized (e.g. <div>, <p>, <input>). React component names are class names, so they should be capitalized (e.g. <SimpleComponent>). This distinction helps you avoid mixing HTML tags with React components.

You already know that JSX is nothing else, but syntactic sugar for React method calls. Without JSX, your code would look like as follows:

For the full transpilation, check out the code in

React.createElement( type, props, ...children ) takes the following arguments:

  • type: a string representing a DOM element tag name, or a React component of type function (class),
  • props: an object of key-value pairs representing the property keys and values of the element (optional),
  • ...children: any number of child node arguments can follow props. A child element can either be a string representing a text node, or a React element

In order to practice a bit, let's transform another JSX expression into React calls:

The equivalent React calls are:

Note that you don't have to know this syntax to become a proficient React developer. It is still handy to know about it.

Our first complex component

From now on, we will branch off from the React boilerplate repository, and use the branch react-chat-example.

Our initial state is here: 2c2ab79.

If you don't like GitHub, you can also build the application together with me using CodePen. You can take this pen as a starting point. Paste the source code in the JavaScript section, and always exclude the import and export statements, as everything will be in the same namespace for you. Whenever you see // ..., make sure you update your component instead of copy-pasting. The text will make you aware of updates before you inspect the corresponding code blocks.

We will now create some components. Let's create the folder src/app/components. We will place all our components here.

Our first component is src/app/components/Message.js:

Let's create a component that displays a list of messages for us, and call it MessageList. The message list component will receive message data from its props.

The map function returns an array of JSX markup. This array can be inserted into the main div of our component. This is the easiest way to render a collection of components.

Notice the key prop. When you deal with a collection of components, we have to supply the key attribute. This attribute is there for efficiency reasons. For more information, refer to the official React documentation.

We have to render the MessageList component in src/app/app.js. We already know that we can pass strings to a React component as props. We will now pass the messages JavaScript array as dynamic props.

Any objects or arrays can be passed to a React component as props. In fact, this is the desired way of establishing communication channels between parent and child components. Child components do not know anything about their parent. Parent components have the ability to pass data to their children. This communication method is called unidirectional data flow or one-way data flow.

Review the change: 4992743d. At any point in the tutorial, you can simply run the code by checking out the displayed commit and executing the npm script launching the webpack-dev-server:

Component states

Let's add a button to the messages component that clears all messages. Disable the button whenever there are no messages to clear. The MessageList component with a disabled clear button looks as follows:

If you had not written code in React before, chances are that you would solve this problem by listening to browser events, and updating the DOM directly.

As React handles DOM manipulations for you, updating the DOM would take away both the performance gain and the maintainability of your code. Once browser events are fired, we update the component state instead of the DOM. React updates the DOM directly.

You can access the state of a component with this.state. You can initialize the state of the component in the constructor.

The object this.state should be treated as read only. If you want to change the state of a React component, use the setState method. It expects an object argument containing the updated state values. Calling setState not only updates the properties, but it also re-renders the component.

Note that if you have more than one state properties, setState only performs an update. Therefore, you don't have to enumerate all state members.

Any action may cause a state change. This includes user interactions and server responses.

Notice the way how we decide on the disabled property of the button. In real life, it makes a lot of sense to create an atomic component for each form element you use. You should have atomic buttons, dropdown lists, textfields, checkboxes, radio buttons etc. This is important in order to abstract the logic.

For the sake of simplicity, we clear the messages in the render method by rendering an empty list of messages ([]). As we cannot add new chat messages yet, this implementation is sufficient for now.

Try to set isClearDisabled to true in the constructor method to observe the cleared state.

Let's implement the click handler that clears our messages:

We can attach click handler functions to DOM elements using the onClick property. Note the context binder in the constructor. This lets us use this as the context of our MessageList instance inside handleClearMessages.

It is generally better to perform all context binding in the constructor than in the render method due to performance reasons.

Decorators are available that perform the binding automatically in case you would like to save the constructor logic:

The click handler sets the state of the component. Calling setState triggers automatic re-rendering. Once render is called, the DOM is updated.

React abstracts event handling for us. The implementation is consistent for all supported browsers. Therefore, we don't have to take care of browser detection, and we can use consistent event syntax throughout our application.

Commit: 822d694.

Component redesign

Our MessageList component is becoming a bit too complex. It is time to encapsulate responsibilities for the sake of extensibility. Otherwise, we might risk encapsulating functionalities in MessageList that don't belong there.

Let's extract a ClearButton component out of the MessageList component.

MessageList.js without the Clear button:


There is not too much to explain about these two components, as you should already be familiar with them.

We will now define a ChatBox component that includes MessageList and the ClearButton components. Let's place the file of the component in src/app/components/ChatBox.js.

We have encapsulated messages inside the scope of ChatBox as its internal state. In the next section, we will get rid of demo data altogether.

We have to place the MessageList and the ClearButton components in a div, as the render method has to return one React element, not a list of elements.

In order to make the application work, we have to render the ChatBox by editing js/app/app.js.

The entry point of the application became a lot shorter. Execute the application using the following commit: d1b488d.

If you have executed the application, you can see that something is wrong: we lost a piece of functionality with this commit. We will fix it in the next section.

Communication between parent and child components

The clear button is not working anymore, as it does not have access to the ChatBox.

ChatBox is the parent component, and ClearButton is the child component. An event happens in the child component, implying a change in the markup of a sibling.

React encourages a solution to pass a prop function from the parent to the child. The child will have access to the prop function of the parent. Calling this function will perform changes in the parent state.

We defined a clearMessages method using the concise syntax of ES6.

In the constructor, we performed the context binding similarly to the other event handlers from before.

In the render method, this.clearMessages is passed to the ClearButton component as a prop.

We will use this prop in the handleClearMessages method of the ClearButton component:

In the handleClearMessages of ClearButton, we call the clearMessages method of the parent. This method sets the state of the parent. Calling setState causes the whole component to rerender. This rerendering clears all chat messages by rerendering the MessageList component with an empty array messages prop.

You can check out the complete code in this commit: 3d3592d.

Let's Chat

Our next task is to define a form that lets us post chat messages. Our static markup looks like this:

We will insert the PostMessageForm between MessageList and the ClearButton.

As we will soon be able to add chat messages, we don't need the default values in the messages array anymore.

ChatBox looks like this after the modifications:

Let's define a submit handler to PostMessageForm. The method is similar to the previously defined click handler, except that we have to prevent the default action of the form submission, in order to avoid a redirection. Also note that we have to add the submit event to the form, not to the submit button.

We have to access the values of the input fields in the submit handler. Technically, we could use document.querySelector to retrieve the corresponding DOM node, and extract its value. This is a suboptimal solution though, as nothing prevents us from observing values from outside the scope of the component. Furthermore, when the markup changes, we also have to change the corresponding queries.

React best practices encourage using refs. Refs allow us to reference our input field values once the component is rendered.

Upon rendering the component, React calls the function in the ref property of each element. The two ref functions place nameInput and messageInput in the namespace of the component.

In order to understand the arrow function syntax in depth, check out my ES6 course, or get five free lessons by signing up below. After confirming the double opt-in, the first lesson explains arrow functions.

Would you like to learn ES6?
Strengthen your JavaScript knowledge with marketable skills!
Get the Course "ES6 in Practice"!

Learn Marketable Skills.

Verify your knowledge with real world exercises.

Thank you for your subscription.
Please check your inbox to access the first lesson.

If you used the ES5 syntax, you would have to write the following equivalent function:

In the handler of the form submission, we cleared the input field values.

We also have to insert the created values in the messages array. The only problem is that accessing the messages array is not the responsibility of PostMessageForm. How can we pass the values of the textfields to the ChatBox?

Similarly to handling the clear button, we will pass a function as a communication channel between the container component and the inner component. We will pass the appendChatMessage function as a prop of the PostMessageForm.

In the appendChatMessage method, we create a new message, and append it to the end of the messages array. Note that I could have used

The reason why I encourage you to use the less performant shallow copy is that you have to treat state as immutable. In order for React to register a state change, the reference to the state value has to differ. If you push a new element to an array, its reference stays the same as before.

You can elegantly clone an array with ES6 using the spread operator: [...arr] is a shallow copy of arr.

In the unlikely case that you don't use ES6, you can use the following method:

Concat returns a new array by concatenating the elements this.state.elements and newMessage.

All we need to do is call the appendChatMessage prop from the submit handler of the form:

Commit: 99b9f60.

Important! There is a conceptual error in Line 17 of src/app/components/PostMessageForm.js in this commit. As you can see, context binding is performed both in the constructor and in the render method. We have already addressed that we should never use context binding in the render method for performance reasons. In this commit, context binding in two places was an obvious error that I overlooked during committing. Until I replace the corresponding commit, this warning will stay in the article.

The code is working well, except that the Clear button state is erronous in two cases:

  • its initial value is enabled even though there are no messages to clear,
  • once we press the clear button, it stays disabled forever.

We will now make the state of the clear button depend on the number of chat messages. All we need to do is add a prop to the ClearButton, and use this prop in the render method of the button.

Now that we link enabling and disabling of the clear button to props, the state of the clear button can be removed. This simplifies the clear button, and eliminates redundancy.

The render method of ChatBox looks like this after the change:

The last change of this article is in the ClearButton, where we will make use of the isDisabled prop in the render method, and simplify the component by eliminating its state.

The last commit of this tutorial is here: f2193a2.

You can also watch the example in action using CodePen.

See the Pen yVxZQW by Zsolt (@zsolt555) on CodePen.

Would you like to learn ES6?
Strengthen your JavaScript knowledge with marketable skills!
Get the Course "ES6 in Practice"!

Learn Marketable Skills.

Verify your knowledge with real world exercises.

Thank you for your subscription.
Please check your inbox to access the first lesson.

You can read the other articles belonging to this series in chronological order by clicking here.

If you would like to get weekly updates on my articles delivered to your inbox, sign up for my ES6 course with your favorite email address.