Building a Chat application from scratch using ReactJs

What is ReactJS?

React is a JavaScript library for building user interfaces. It is maintained by Facebook and a community of individual developers and companies. — Wikipedia

In simple terms, HTML + JS + CSS = React

Why ReactJS?

I had recently moved to a role where I entirely do the backend with Python. Though I prefer it, I don’t want my JS skills to go in vain. Most of my friends are front-end developers, so its a bit of peer pressure to know their language(both in terms of React and what they talk). I wanted to build my own portfolio website using Gatsby which is built using React. Bonus point: The frontend developer I’m working with can’t just say, “that can’t be done in react” and walk away like that.

Pre-requisites

You need to have an understanding of front-end technologies like HTML, CSS, and JS. You don’t have to master all of them. If you know how to put them together and create a webpage, you’re good to go.

ReactJs — A Basic Understanding

React = Component + props + state

Three basic terminologies you would come across as a beginner is component, state, and props. Apart from these, I hear fancy names like “Lazy React”, “React hooks” etc., which are in my list but not in this blog.

Where to Start?

I am a documentation freak. I spend an insane amount of time with the documentation and look for other resources/tutorials only if it doesn’t work.

React’s documentation tutorial walks you through a tic tac toe game and teaches you how to organize your components and play around with its states and props. Though I completed the tutorial I had no clue how to apply it to my project.

What worked finally for me was thinking in react. It teaches you by splitting a web page components and how to intervene states and props across them.

What’s great about this tutorial is that it’s very close to reality. A front-end developer will always have a design to work with. So starting React with an empty slate like in Tic Tac Toe was not much of a help.

What are we building?

I had this common front-end chat module built using plain HTML, CSS, and JS. It had been my template to build and play around with chatbots. Most of my chatbot hobby projects have this front-end.

My end goal was to achieve a chat frontend that would send and receive messages to a backend API. You can directly jump and play around with the code in my GitHub repository.

Thinking In Components

The first step to start with is to define individual UI components. What do you mean you may ask?

In the above picture, we can see around 4–5 UI elements. These are wrapped into individual entities called components.

Send button

class SendButton extends Component{

render(){

return (

<div className="send_message"

<div className="text">send</div>

</div>);

}

}

TextBox

class MessageTextBoxContainer extends Component{

render(){

return(

<div className="message_input_wrapper">

<input id="msg_input"

className="message_input"

placeholder="Type your messages here..."/>

</div>

);

}

}

Avatar

A place holder for DP.

The avatar is where the profile picture of the person goes. For now, we are going to stick with different backgrounds.

class Avatar extends Component {

render(){

return(

<div className="avatar"/>

);

}

}

The Message

The MessageBox component has an Avatar component in it. For each message, we will just loop through and create N of these components.

class MessageBox extends Component{

render(){

return(

<li className={`message ${this.props.appearance} appeared`}>

<Avatar></Avatar>

<div className="text_wrapper">

<div className="text">{this.props.message}</div>

</div>

</li>

);

}

}

The Whole App

Similarly, we bring together the whole UI by binding these components together. It has a MessageContainer where are the messages are listed, The TextBox and SendButton. For now, ignore the this.state or this.handleClick

class ChatApp extends Component {

render() {

return (

<div className="chat_window">

<MessagesContainer messages={this.state.messages}/>

<div className="bottom_wrapper clearfix">

<MessageTextBoxContainer/>

<SendButton handleClick={this.handleClick}/>

</div>

</div>

);

}

}

Event Capturing

Before moving on bumping our heads with states and props of the component let’s see what events we need to handle.

Send_message on pressing the send button. Send_message on pressing enter.

In this block, we will explore how to capture the event press_enter and send_button_click event

send button from the app

Let’s go back to our SendButton component and attach a method to handle clicks. Similarly, we can add an onKeyPress event to the textbox and capture the events.

class SendButton extends Component{

handleClick(){

console.log("I am captured");

} render(){

return (

<div className="send_message"

onClick={this.props.handleClick}>

<div className="text">send</div>

</div>);

}

}

Event Handling

Now that we have captured the button click and keypress events. Let’s see how to handle those events. This is the part where I struggled the most.

On clicking send or on pressing enter, the current message in TextBox should be added as a MessageComponent to MessageContainer component.

Now let’s go through each of these components and see what are data that we need to populate the required information. This data is defined by the state and props of the component.

Deriving States and Props

Let’s take each component and see what data does it need to do its job well.

SendButton — Needs access to current_message user is typing in order to send it to the chatbot. MessageTextBox — Needs to maintain and update current_message as and when the user types it (State) and sends it to the chatbot on pressing enter MessageContainer — Needs to maintain a list of all the messages from both the bot and the user to populate it.

In order to populate the current message in MessageContainer we need to know the current message that the user had typed in the MessageTextBox.

The MessageContainer also needs to keep track of all the messages that have been typed/received so far. hence, it would have messages an array as its property.

class TextBox extends Component{ constructor(props){

this.state.current_message = ""

} onChange(e) {

this.setState({

current_message: e.target.value;

});

if(e.key === "Enter"){

// We need to add a new message to MessageContainer component

} }

render(){

return(

<div className="message_input_wrapper">

<input ...

value={this.props.current_message}

onChange={this.props.onChange}/>

</div>

);

}

}

Now you can see that initially, the current_message is an empty string and onChange, as we type the text into our TextBox component. But the changes you make in the TextBox component is not visible to MessageContainer

In React, sharing state is accomplished by moving it up to the closest common ancestor of the components that need it. This is called “lifting the state up”.

Lifting State Up

Now comes the question of which component should we move the control to? Let’s draw a simple tree of the components and see where what fits.

On drawing this out, it is pretty obvious that all the child components need access to current_message and list of messages already seen by the bot. Thankfully, we have only one parent ChatApp which will delegate all the state and event handling requirements of to the respective child components

Out ChatApp component is going to get a bit messy right now. First, let’s define all the functions we need and later attach them to events as we need them.

addMessageBox(enter=true){

let messages = this.state.messages;

let current_message = this.state.current_message;

if(current_message && enter){

messages = [...messages, {"message":current_message}];

} handleClick(){

this.addMessageBox();

} _handleKeyPress(e) {

let enter_pressed = false;

if(e.key === "Enter"){

enter_pressed = true;

}

this.addMessageBox(enter_pressed)

} render() {

return (

<div className="chat_window">

<MessagesContainer messages={this.state.messages}/>

<div className="bottom_wrapper clearfix"> .

<MessageTextBoxContainer

_handleKeyPress={this._handleKeyPress}

onChange={this.onChange}

message={this.state.current_message}> .

</MessageTextBoxContainer> <SendButton handleClick={this.handleClick}/>

</div>

</div>

);}