• Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others
No Result
View All Result
Codershood
No Result
View All Result

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 3

Connecting Angular application to Socket Server and Implementing Real-time chat list

by Shashank Tiwari
October 20, 2019
in ReactJs
5
10 Minutes Read
Real time private chatting app using React, Nodejs, mongodb and Socket.io banner

This is the third part of the Building Real time private chatting app using React. In this part, you will implement below-listed features in your application,

  1. You will connect your React application to Socket Server.
  2. You will implement the Realtime chat list for the application.
  3. You will write Socket Server Event to Emit the updated Chat List.
  4. Highlight the select user from the list of users

Before Going any further a little recap,
In the previous part of the series, we completed the Login/Registration process in our application. After Login/Registration, the application will redirect the user to a home page of the application. Once you redirected to the homepage, the application will check the session of the user by making an HTTP request.

As soon as the application finishes the session checking, we will connect this React to the Nodejs socket server. Once the socket server is connected, just after that we will subscribe to real-time chat list update. So let’s get our hands dirty and start integrating real-time goodness into our application.

If you are familiar with Angular, then you will like to readhow to create Real-time private chatting app using Angular.

 Download

 Demo

 




 

Connecting React application to Socket Server

We won’t just connect the Socket server by using below code, I know in most of the article you have seen, read and wrote this code to connect the Socket Server.

const socket = io('localhost:3000');

Here we will add a little extra sugar by passing a parameter while connecting to the socket server.

=>Which we will do by using the code shown below,

this.socket = io(this.BASE_URL, { query: `userId=${userId}` });

Now you may ask Why we need to this in the first place?
Well, the answer is simple. We need userId to identify the which user is just connected to the socket Server. Also, we will update the socket id of the connected user in MongoDB.

=> The updated socketId will be used at the different stages of the application.

=>Since you have everything ready to go, all you have to do is call establishSocketConnection()(This method is defined inside the chatSocketServer.js file) from HomeComponent class.

Open the home.js file and replate the componentDidMount method by below code inHomecomponent class,

home.ts:

/*
* Real time private chatting app using React, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/
async componentDidMount() {
    try {
      this.setRenderLoadingState(true);
      this.userId = await ChatHttpServer.getUserId();
      const response = await ChatHttpServer.userSessionCheck(this.userId);
      if (response.error) {
        this.props.history.push(`/`)
      } else {
        this.setState({
          username: response.username
        });
        ChatHttpServer.setLS('username', response.username);
        ChatSocketServer.establishSocketConnection(this.userId);
      }
      this.setRenderLoadingState(false);
    } catch (error) {
      this.setRenderLoadingState(false);
      this.props.history.push(`/`)
    }
}

As I said earlier, we will update the socketId of the user in the MongoDB database, hence we can use it later in the application. If you check the socket connection string in the network tab, you should see something similar to the below-shown image.

As of now, I am using the Chrome browser. Though it has nothing to do with what kind of browser you use. Anyways make sure you are passing userid while connecting socket server.

Userid parameter in socket Real time private chatting app using React Nodejs mongodb and Socket.ioPassing user id in socket connection

 

Now you have provided a userId as a parameter in socket connection. Let’s complete abstract work in server side.

Open the socket.js file and add the below code to it,

socket.js:

/*
* Real time private chatting app using React, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/socketConfig(){
    this.io.use( async (socket, next) => {
        try {
            await queryHandler.addSocketId({
                userId: socket.request._query['userId'],
                socketId: socket.id
            });
            next();
        } catch (error) {
            // Error
            console.error(error);
        }
      });

    this.socketEvents();
}

Explanation:

  • The first thing to notice here we are reading the userId parameter by using line shown below,
    socket.request._query['userId']
  • Second, we haveaddSocketId() method to update the data in MongoDB.
  • And at the end, we have called rest of the socket server Event by callingthis.socketEvents() method.

Implementing a Realtime chat List

I have divided this section into the smaller sections, since covering everything into a single section would be very nosogenic to read and understand. Creating chat list involves below-listed points,

  1. Creating ChatList component.
  2. Writing Markup for a chat list
  3. WritingChatList component Class.
  4. Writing Socket server Event.
  5. Updating user’s online status in ChatList
  6. Selecting a User from the chat list of the user.

By the end of this article, you will have full full-fledged working chat list for your application. So let’s dig our teeth into it and finish it.

Creating ChatList component

Why a new component for ChatList? Though we can write all the markup and Code inside the Home Component. But, It’s not a good practice and your Home Component class will be heavy and long to manage.

=> Therefore first things first, you have to createChatListcomponent nothing so special about it.

=>Let’s import theChatListcomponent intoHomeComponent. Open the home.js and add the below line, so that you can show chatlist in your application,

home.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
import ChatList from './chat-list/ChatList';

Now you add the<ChatList> into your home.js file, and So at this point, your home.jsfile should look like this.

home.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
import React, { Component } from 'react';
import { withRouter } from "react-router-dom";

import ChatSocketServer from '../../utils/ChatSocketServer';
import ChatHttpServer from '../../utils/ChatHttpServer';

import ChatList from './chat-list/ChatList';

import './Home.css';

class Home extends Component {
  userId = null;
  state = {
    isOverlayVisible: true,
    username: '______',
  }


  setRenderLoadingState = (loadingState) => {
    this.setState({
      isOverlayVisible: loadingState
    });
  }

  async componentDidMount() {
    try {
      this.setRenderLoadingState(true);
      this.userId = await ChatHttpServer.getUserId();
      const response = await ChatHttpServer.userSessionCheck(this.userId);
      if (response.error) {
        this.props.history.push(`/`)
      } else {
        this.setState({
          username: response.username
        });
        ChatHttpServer.setLS('username', response.username);
        ChatSocketServer.establishSocketConnection(this.userId);
      }
      this.setRenderLoadingState(false);
    } catch (error) {
      this.setRenderLoadingState(false);
      this.props.history.push(`/`)
    }
  }

  getChatListComponent() {
    return this.state.isOverlayVisible ? null : <ChatList userId={this.userId} updateSelectedUser={this.updateSelectedUser}/>
  }

  render() {
    return (
      <div className="App">
        <div className = {`${this.state.isOverlayVisible ? 'overlay': 'visibility-hidden' } `}>
          <h1>Loading</h1>
        </div>
        <header className="app-header">
          <nav className="navbar navbar-expand-md">
            <h4>Hello {this.state.username} </h4>
          </nav>
          <ul className="nav justify-content-end">
            <li className="nav-item">
              <a className="nav-link" href="#" >Logout</a>
            </li>
          </ul>
        </header>

        <main role="main" className="container content" >
          <div className="row chat-content">
            <div className="col-3 chat-list-container">
              {this.getChatListComponent()}
            </div>
            <div className="col-8 message-container">
            </div>
          </div>
        </main>
      </div>
    );
  }
}

export default withRouter(Home)

Writing Markup for a chat list

Open thechat-list.jsand add the below-shown render method. The below code is ridiculously easy to understand.

=> The next thing ischatListUsersproperty of ChatList Component class.

=> InchatListUsersproperty we will store the list of the users. This variable is nothing but an array, which updates itself on certain stipulated conditions.

ChatList.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
render() {
    return (
      <>
        <ul className={`user-list ${this.state.chatListUsers.length === 0 ? 'visibility-hidden' : ''}`} >
          {
            this.state.chatListUsers.map( (user, index) => 
              <li 
                key={index} 
                className={this.state.selectedUserId === user.id ? 'active' : ''}
              >
                {user.username}
                <span className={user.online === 'Y' ? 'online' : 'offline'}></span>
              </li>
            )
          }
        </ul>
        <div className={`alert 
          ${this.state.loading ? 'alert-info' : ''} 
          ${this.state.chatListUsers.length > 0 ? 'visibility-hidden' : ''}`
        }>
          { this.state.loading|| this.state.chatListUsers.length.length === 0 ? 'Loading your chat list.' : 'No User Available to chat.'}
        </div>
      </>
    );
}

Writing ChatList component Class

At this point, your ChatList component will render nothing. So to make it work we will add some code to the ChatList component class. Basically, in this class, do two important things.

=>First we will update the chat list and the second, we will pass the data of the selected user from the list of users to the Conversation component.

=>But first, let’s add the necessary state properties required in ChatList component class, open the ChatList.js and add the below properties,

ChatList.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
constructor(props) {
    super(props);
    this.state = {
      loading: true,
      selectedUserId: null,
      chatListUsers: []
    }
}

Now let’s add the code to update the chat list, this code will basically manipulate thechatListUsers property. So open the ChatList.js and add the below code.

ChatList.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
componentDidMount() {
    const userId = this.props.userId;
    ChatSocketServer.getChatList(userId);
    ChatSocketServer.eventEmitter.on('chat-list-response', this.createChatListUsers);
}

componentWillUnmount() {
    ChatSocketServer.eventEmitter.removeListener('chat-list-response', this.createChatListUsers);
}

createChatListUsers = (chatListResponse) => {    
    if (!chatListResponse.error) {
        let chatListUsers = this.state.chatListUsers;
        if (chatListResponse.singleUser) {
            if (chatListUsers.length > 0) {
                chatListUsers = chatListUsers.filter(function (obj) {
                    return obj.id !== chatListResponse.chatList[0].id;
                });
            }
            /* Adding new online user into chat list array */            chatListUsers = [...chatListUsers, ...chatListResponse.chatList];
        } else if (chatListResponse.userDisconnected) {
            const loggedOutUser = chatListUsers.findIndex((obj) => obj.id === chatListResponse.userid);
            if (loggedOutUser >= 0) {
                chatListUsers[loggedOutUser].online = 'N';
            }
        } else {
            /* Updating entire chat list if user logs in. */            chatListUsers = chatListResponse.chatList;
        }
        this.setState({
            chatListUsers: chatListUsers
        });
    } else {
        alert(`Unable to load Chat list, Redirecting to Login.`);
    }
    this.setState({
        loading: false
    });
}

render() {
    // you have already wrote the markup in the above section
}

We know what the above code does, let’s understand how the above code works!

Explanation :

  1. UsinggetChatList()method we are updating the list of the user, which is defined into the ChatSocketService service.
  2. For example, If a new user logs in into the system or an existing user goes offline in both the cases we will update the list of online users.
  3. Here I am emitting the complete list of online/offline users from the nodejs socket server to the user who justlogged into the system.
  4. And, for the rest of users, those who are already online will get the users information whowent offlineorjust logged in. This makes sense, right? Because sending the complete list of online users to each and every user is not good.
  5. Now if you see the above code there iselse if()code block,for the below task.
  6. If the response from the socket event contains thesingleUser propertywhich means the new socket connection is established.
  7. If the response contains theuserDisconnected propertythen it means the user went and offline remove the user from the chat list.
  8. And at the end, In the else condition we will update the list of users in the else block.

Writing Socket Server Events to Emit the updated Chat List

All the above only happen if you have Socket server and which responds to the updated chat list. Anyways, let’s go ahead and talk something constructive.

=> Here you will write socket.io.onlistener on the server side when a client requests it.

=> Once React asks for chat list to Socker server, the socket will query the details from the database.

=>After querying, based on the user it emits the result.

So this was the basic idea, let’s see the code to implement the same. Open the socket.jsof your nodejs project and add the below code,

socket.js:

/*
* Real time private chatting app using React, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/socket.on(`chat-list`, async (data) => {
    if (data.userId == '') {
        this.io.emit(`chat-list-response`, {
            error : true,
            message : CONSTANTS.USER_NOT_FOUND
        });
    }else{
        try {
            const [UserInfoResponse, chatlistResponse] = await Promise.all([
                    queryHandler.getUserInfo( {
                        userId: data.userId,
                        socketId: false
                    }),
                    queryHandler.getChatList( socket.id )
                ]);
            this.io.to(socket.id).emit(`chat-list-response`, {
                error : false,
                singleUser : false,
                chatList : chatlistResponse
            });
            socket.broadcast.emit(`chat-list-response`,{
                error : false,
                singleUser : true,
                chatList : UserInfoResponse
            });
        } catch ( error ) {
            this.io.to(socket.id).emit(`chat-list-response`,{
                error : true ,
                chatList : []
            });
        }
    }
});

Explanation:

The above code seems easy to understand. But me being me, am gonna explain it anyway.

  1. First thing first, we will check for the validation. If all goes well, process ahead else emit a response with an error message.
  2. Second, inside the try-catch block, we will execute all the things. Why ? because you never know when your code will explode.
  3. Just kidding,try-catch is there to handle the unknown exceptions and unhandled promise rejections. And I feel like it’s a good practice to follow.
  4. Then inside the try-catch block, you will first fetch the information about the user by calling getUserInfo(). Later you all thegetChatList() which is defined inside the QueryHandler class.
  5. Why are you emitting two socket Events?As I explained in the above section, you don’t wanna send the entire chat list to the user instead send only information about the user who goes online/offline.

Let’s take a look at those method defined in theQueryHandlerclass. Open the query-handler.js and add the below code,

In the below code, We are just executing a MongoDB query. Based on the socketId parameter, we are projecting the result from the database.

query-handler.js:

/*
* Real time private chatting app using React, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/getUserInfo({userId,socketId = false}){
    let queryProjection = null;
    if(socketId){
        queryProjection = {
            "socketId" : true
        }
    } else {
        queryProjection = {
            "username" : true,
            "online" : true,
            '_id': false,
            'id': '$_id'
        }
    }
    return new Promise( async (resolve, reject) => {
        try {
            const [DB,ObjectID] = await this.Mongodb.onConnect();
            DB.collection('users').aggregate([{
                $match:  {
                    _id : ObjectID(userId)
                }
            },{
                $project : queryProjection
            }
            ]).toArray( (err, result) => {
                DB.close();
                if( err ){
                    reject(err);
                }
                socketId ? resolve(result[0]['socketId']) : resolve(result);
            });
        } catch (error) {
            reject(error)
        }
    });
}

Now the last method left is to fetch the chat list from the database. Honestly Speaking you are just fetching all the user from the database and displaying it to the front end based on the online/offline status.

Open the query-handler.js and add the below code. In the below code, we will fetch all the records from the database.

query-handler.js:

/*
* Real time private chatting app using React, Nodejs, mongodb and Socket.io
* @author Shashank Tiwari
*/
getChatList(userId){
    return new Promise( async (resolve, reject) => {
        try {
            const [DB,ObjectID] = await this.Mongodb.onConnect();
            DB.collection('users').aggregate([{
                $match: {
                    'socketId': { $ne : userId}
                }
            },{
                $project:{
                    "username" : true,
                    "online" : true,
                    '_id': false,
                    'id': '$_id'
                }
            }
            ]).toArray( (err, result) => {
                DB.close();
                if( err ){
                    reject(err);
                }
                resolve(result);
            });
        } catch (error) {
            reject(error)
        }
    });
}

In the above code, we are executing the MongoDB query. This query will fetch the list of users from the database by using the given condition. After executing the query, it will return the result trough Promise.

Once you implement all this you should be properly working chat list as shown in below image,

Userid parameter in socket Real time private chatting app using React Nodejs mongodb and Socket.io

 

Realtime Chat list

Selecting a user from the chat list to chat

Till now you have successfully implemented the chat list feature in your application. Let’s go one more step ahead and add the feature to highlight the user selected for the chatting.

=>In this section we will just highlight the selected user from the chat list.

=> Open the ChatList.jsand replace the code, Here we are using bootstrap classes, so you don’t need to write any CSS for that.

ChatList.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
render() {
    return (
        <>
            <ul className={`user-list ${this.state.chatListUsers.length === 0 ? 'visibility-hidden' : ''}`} >
            {
                this.state.chatListUsers.map( (user, index) => 
                    <li 
                        key={index} 
                        className={this.state.selectedUserId === user.id ? 'active' : ''}
                        onClick={() => this.selectedUser(user)}
                    >
                        {user.username}
                        <span className={user.online === 'Y' ? 'online' : 'offline'}></span>
                    </li>
                )
            }
            </ul>
            <div className={`alert 
                    ${this.state.loading ? 'alert-info' : ''} 
                    ${this.state.chatListUsers.length > 0 ? 'visibility-hidden' : ''}`
            }>
                { this.state.loading|| this.state.chatListUsers.length.length === 0 ? 'Loading your chat list.' : 'No User Available to chat.'}
            </div>
        </>
    );
}

Explanation:

  1. As you can see the method this.state.selectedUserId will return true or false, and with the help of that, we will show currently selected user.
  2. By comparing the user selected userId with the user id.

=>In ChatListComponent class, we have selectedUserId state property as shown below.

selectedUserId: null,

This property will hold the value of the selected user id. To complete this feature, add the below methods in the ChatList.js.

ChatList.js:

/* Real time private chatting app using React, Nodejs, mongodb and Socket.io
*  @author Shashank Tiwari
*/
selectedUser = (user) => {
    this.setState({
        selectedUserId: user.id
    });
    this.props.updateSelectedUser(user)
}

Explanation:

  1. selectedUser() method will update the selectedUserIdstate property. Also, this method will emit the latest selected user id using the behavior subject.
  2. Also, this method will call a props method, i.e. updateSelectedUser which will give the currently selected user to its parent component.

Finishing Note

I believe this part of the series was short as compare to others. Let’s recall one more time, what we implemented here before wrapping this part. We implemented below-listed features in our application,

  1. We connected our React application to Socket Server.
  2. We implemented the Realtime chat list for the application.
  3. We wrote Socket Server Event to Emit the updated Chat List.
  4. Highlighted the select user from the list of users

For now, that’s it. I’ll see you in next and the last of the series where will implement real-time chat feature.

 

 

 

 

Previous Post

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 2

Next Post

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 4

Related Posts

Show better loading screen in React using Suspense and SuspenseList
ReactJs

Show better loading screen in React using Suspense and SuspenseList

May 1, 2020
Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 4
ReactJs

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 4

October 31, 2019
Real time private chatting app using React, Nodejs, mongodb and Socket.io banner Part 2
ReactJs

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 2

October 20, 2019
Next Post
Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 4

Real time private chatting app using React, Nodejs, mongodb and Socket.io – Part 4

Comments 5

  1. CYI says:
    6 years ago

    Hi, may I know when will the last part will be posted?

    Reply
    • Shashank Tiwari says:
      6 years ago

      Hey I just did, https://codershood.info/2019/10/31/real-time-private-chatting-app-using-react-nodejs-mongodb-and-socket-io-part-4/

      Reply
  2. AF_Web says:
    5 years ago

    Hi I left a comment on part 4 also, your source code doesn’t seem to work, have you tested it?

    I get errors about toLowerCase() of undefined

    if I remove it and try to call api using postman I get server error, can’t create user..

    Reply
    • Shashank Tiwari says:
      5 years ago

      Hey Can you post the error? And just to check you have downloaded the code from Github?

      Reply
  3. AF_Web says:
    5 years ago

    Hi, yes I cloned the git repo, below is a list of instructions that I complete:

    1) Run ‘npm install’ for both the React application and the server
    2) For both folders (react app and server) I have to update some dependencies as they are outdated and no longer available. For example bcrypt is now bcryptjs I believe.
    3) I run ‘npm start’ for react application and have to fix some filenames as you have specified the file name having a capital letter at the beginning when it doesn’t have one so that throw multiple errors.
    4) After fixing the filenames I try and run ‘npm start’ again, this time I get an error which reads:
    *Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it’s defined in, or you might have mixed up default and named imports.

    Check the render method of `Login`* <— I get the same for Registration

    Reply

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *




https://codershood.info

www.codershood.info programming blog dedicated to providing high-quality coding tutorial and articles on web development, Angular, React, Laravel, AngularJs, CSS, Node.js, ExpressJs and many more. We also provide ebook based on complicated web application along with the source code.

  • Download
  • Contact
  • Terms of Service
  • Privacy Policy
  • About US

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.

No Result
View All Result
  • Demos
  • Plugins
  • Angular
  • NodeJs
  • GO lang
  • Others

www.codershood.info is licensed under a Creative Commons Attribution 4.0 International License.