How I Created a Video Chat App using 100ms React SDK
Tags: webdev
Published: 2021-09-29 13:32:21

![JoinForm page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ij2fs3b5i4t6t4qg2uha.png)


> In this blog, I would like to burst out the popular belief, that building your own video chat app is too challenging.

_Before we start building, let me explain briefly, what is [` 100ms `](https://www.100ms.live/) ?_

100ms is a cloud platform that allows developers to add video and audio conferencing to Web, Android and iOS applications.

The platform provides REST APIs, SDKs, and a dashboard that makes it simple to capture, distribute, record, and render live interactive audio, video.

Check out the 100ms [` Documentation `](https://docs.100ms.live/javascript/v2/foundation/basics) here.



#####Basic Terminology to keep in mind 


* `hmsStore` - will hold the complete state of the application such as details of all the participants. We can also visualize this state at any time using the devtools extension.
* `hmsActions` - will help us perform actions such as joining the room, mute our audio and send messages.
* `Room` - When we join a conference call, the participants are said to be in a video call room.
* `Peer` - A participant in the video call. You are the local peer while others are remote peers.
* `Track`- Media. There are two types of track a peer can have - audio and video.

##Now getting Started 

Our Video Chat app will have the following features:
  
    1. Video/Audio calling
    2. Realtime Chat

* Create your react app 

` npx create-react-app my-app `
` cd my-app `
` npm start `

* Intsalling the dependencies

`npm install --save @100mslive/hms-video@latest @100mslive/hms-video-react@latest
`


* Now in the `src/Index.js` - we're rendering everything to the 'root' element and also wrapping the entire application  with `<HMSRoomProvider />` component (which will let us use the hooks for state and actions)

```javascript

import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { HMSRoomProvider } from "@100mslive/hms-video-react";
import App from "./App";

const rootElement = document.getElementById("root");

ReactDOM.render(
  <HMSRoomProvider>
      <App />
  </HMSRoomProvider>,
  rootElement
);

```
* Now create a `joinForm.js` component, to join a room, and for that in the component, we need to call the join method on hmsActions. And add this component to the `App.js`.

**we will have a form that includes two input fields accepting** :

`Username` - The name of the user. This is the value that will be set on the peer object and be visible to everyone connected to the room.
`authToken` - A client-side token that is used to authenticate the user. You can read about how to generate this token [` here `](https://docs.100ms.live/javascript/v2/guides/token) 

```javascript

import { useState } from "react";

import {
  useHMSActions,
  useHMSStore,
  selectIsLocalAudioEnabled,
  selectIsLocalVideoEnabled,
} from "@100mslive/hms-video-react";

const JoinForm = () => {
  const hmsActions = useHMSActions();
  const [inputValues, setInputValues] = useState({
    name: "",
    token: "",
  });

  const handleInputChange = (e) => {
    setInputValues((prevValues) => ({
      ...prevValues,
      [e.target.name]: e.target.value,
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    hmsActions.join({
      userName: inputValues.name,
      authToken: inputValues.token,
    });
  };

  return (
    <>
    <div className="showcase">
    <video autoPlay muted loop >
         <source src={backgroundVideo} type="video/mp4" />
    </video>
    <div className="overlay"></div>
    <form onSubmit={handleSubmit}>
      <h2 className="heading">Join Room</h2>
      <div className="input-container">
        <input
          className="inputfield"
          value={inputValues.name}
          onChange={handleInputChange}
          id="name"
          type="text"
          name="name"
          placeholder="Your name"
        />
      </div>
      <div className="input-container">
        <input
          className="inputfield"
          value={inputValues.token}
          onChange={handleInputChange}
          id="token"
          type="text"
          name="token"
          placeholder="Auth token"
        />
      </div>
      <button className="btn-primary">Join</button>
    </form>
    </div>
    </>
  );
};

export default JoinForm;

```
`Note` - This code includes my css styled classes

* let us now create `Room.js` component that will give us the list of peers and we'll render them.

```javascript
import { useState, useEffect } from "react";
import { selectPeers, useHMSStore } from "@100mslive/hms-video-react";
import Peer from "./Peer"; 
import Chat from "./chat"; //will be imported
import ButtonBar from "./ButtonBar"; //will be imported

const Room = ({ toggleChat, setToggleChat }) => {
  const peers = useHMSStore(selectPeers);

  return (
    <div className="grid-container" style={{ backgroundColor: "#1B2156" }}>
      <div className="peers-container">
        {peers.map((peer) => (
          <Peer key={peer.id} peer={peer} />
        ))}
      </div>
      // {below} - will be added when creating the chat component
      <div className="chatbox-container">{toggleChat ? <Chat /> : null}</div>
    </div>
  );
};

export default Room;
```

Also, create  a `Peer.js` component, where we will be rendering video tiles in the peer-container.

```javascript
import {
  selectVideoTrackByPeerID,
  useHMSActions,
  useHMSStore,
} from "@100mslive/hms-video-react";
import { useRef, useEffect } from "react";

const Peer = ({ peer }) => {
  const videoRef = useRef(null);
  const hmsActions = useHMSActions();
  const videoTrack = useHMSStore(selectVideoTrackByPeerID(peer.id));
  
  useEffect(() => {
    if (videoRef.current && videoTrack) {
      if (videoTrack.enabled) {
        hmsActions.attachVideo(videoTrack.id, videoRef.current);
      } else {
        hmsActions.detachVideo(videoTrack.id, videoRef.current);
      }
    }
  }, [videoTrack, hmsActions]);

  return (
    <div className="peer-container">
      <video
        ref={videoRef}
        className={`peer-video ${peer.isLocal ? "local" : ""}`}
        autoPlay
        muted
      />
      <div className="peer-name">
        {peer.name} {peer.isLocal ? "(You)" : ""}
      </div>
    </div>
  );
};

export default Peer;
```

* In the `App.js` component, we will also be adding leaving room functionality, which we can do by setting up `leave` method on hmsActions.

```javascript

import JoinForm from "./components/JoinForm";
import "./styles.css";
import Room from "./components/Room";
import Notification from "./components/Notifications";
import { useEffect } from "react";
import {
  selectIsConnectedToRoom,
  useHMSActions,
  useHMSStore,
} from "@100mslive/hms-video-react";
import ButtonBar from "./components/ButtonBar";

const App = () => {
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  const hmsActions = useHMSActions();

  useEffect(() => {
    window.onunload = () => {
      if (isConnected) {
        hmsActions.leave();
      }
    };
  }, [hmsActions, isConnected]);

  return (
    <div className="App" >
      {isConnected ? (
        <>
          //these components will be added later
          <Notification />
          <Room />
          <ButtonBar />
        </>
      ) : (
        <JoinForm />
      )}
    </div>
  );
};

export default App;
```
* Now it's time to add the buttons bar component, which will include Mute/Unmute button, leave room button and Chatbox button.

```javascript

import React, { useState, useEffect, useMemo } from "react";
import { BiMicrophone } from "react-icons/bi";
import { BiMicrophoneOff } from "react-icons/bi";
import { BiVideo } from "react-icons/bi";
import { BiVideoOff } from "react-icons/bi";
import { AiOutlineSetting } from "react-icons/ai";
import { FaPhone } from "react-icons/fa";
import { MdScreenShare } from "react-icons/md";
import { MdStopScreenShare } from "react-icons/md";

import {
  useHMSActions,
  useHMSStore,
  selectPeers,
  selectLocalPeer,
  selectIsLocalAudioEnabled,
  selectIsConnectedToRoom,
  selectIsLocalVideoEnabled,
} from "@100mslive/hms-video-react";
import Chat from "./chat";

const ButtonBar = ({ peer }) => {
  const peers = useHMSStore(selectPeers);
  const localPeer = useHMSStore(selectLocalPeer);
  const videoEnabled = useHMSStore(selectIsLocalVideoEnabled);
  const audioEnabled = useHMSStore(selectIsLocalAudioEnabled);
  const hmsActions = useHMSActions();
  const isConnected = useHMSStore(selectIsConnectedToRoom);
  const [toggleChat, setToggleChat] = useState(false);


  const toggleAudio = () => {
    hmsActions.setLocalAudioEnabled(!audioEnabled);
  };

  const toggleVideo = () => {
    hmsActions.setLocalVideoEnabled(!videoEnabled);
  };

  return (
    <>
      <div className="control-bar">
        <button
          className="chatbox-btn"
          onClick={() => setToggleChat(!toggleChat)}
        >
          In-call Messages
        </button>
        {toggleChat ? <Chat /> : null}
        <div className="center-buttons">
          <nav>
            <button className="btn-control" onClick= {toggleAudio}>
              {audioEnabled ? (
                <BiMicrophone size="1.8em" />
              ) : (
                <BiMicrophoneOff size="1.8em" />
              )}
            </button>
            <button
              className="btn-control"
              alt="Leave Room"
              onClick={toggleVideo}
            >
              {videoEnabled ? (
                <BiVideo size="1.8em" />
              ) : (
                <BiVideoOff size="1.8em" />
              )}
            </button>
          </nav>
          {isConnected && (
            <FaPhone
              size="2em"
              id="leave-btn"
              className="btn-danger"
              onClick={() => hmsActions.leave()}
            />
          )}
        </div>
        <div className="App-name">VidChat</div>
      </div>
    </>
  );
};

export default ButtonBar;

```

`Note` - That we have to create a chat component which will be included in the button bar. Also, I have added React icons for styling. 

* Creating a `chat.js` component for In-call messaging, using `await hmsActions.sendBroadcastMessage('message')` which will 
send a message to every peer in the room and use `useHMSStore (selectHMSMessages)`.

```javascript

import React from "react";
import { useState, useEffect } from "react";
import {
  useHMSActions,
  useHMSStore,
  selectLocalPeer,
  selectPeers,
  selectHMSMessages,
  selectMessagesByRole,
  selectMessagesByPeerID,
  selectBroadcastMessages,

} from "@100mslive/hms-video-react";
import "dayjs/locale/es";
import * as dayjs from "dayjs";

const relativeTime = require("dayjs/plugin/relativeTime");
const localizedFormat = require("dayjs/plugin/localizedFormat");
dayjs.extend(relativeTime);
dayjs.extend(localizedFormat);

const Chat = () => {
  const hmsActions = useHMSActions();
  const getMessages = useHMSStore(selectHMSMessages);
  const [inputMessage, setInputMessage] = useState("");

  const typedMessages = async () => {
    await hmsActions.sendBroadcastMessage(inputMessage);
    setInputMessage("");
  };

  const handleOnKeyDownEvent = (e) => {
    if (e.keyCode === 13) {
      typedMessages(inputMessage);
      setInputMessage("");
    }
  };
  function renderMessages(data) {
    console.log(data);
    return (
      <>
        <div>
          <div className="message-sender" key={data.id}>
            {data.senderName}
            <div className="date">{dayjs(data.time).format("LT")}</div>
          </div>
          <div className="message ">{data.message} </div>
        </div>
      </>
    );
  }

 

  return (
    <>
      <div className="chat">
        <div className="chat-title">
          <h2> Inbox </h2>
        </div>

        <div className="messages">
          <div className="messages-content scrollbar ">
            {getMessages.map(renderMessages)}
          </div>
        </div>

        <div className="message-box">
          <textarea
            type="text"
            className="message-input"
            placeholder="Type a message . . ."
            value={inputMessage}
            onChange={(e) => setInputMessage(e.target.value)}
          ></textarea>
          <button className="message-submit" type="button" onClick={typedMessages}>
            Send
          </button>
        </div>
      </div>
    </>
  );
};

export default Chat;

```

Add this component to the button bar. 

`Note` - `I have used Day.js for getting formatted time in the chatbox`

###Video calling view -

![Main screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4nhqauhsvadbnqod7jn9.jpeg)

-----
Some other features that can be added are - 
 * Notifications
 * Screen-Sharing
 * Show Audio levels
 
Hope this article is useful. Feel free to reach out to me for any queries.

**Full Code on Github -**
{% github MdMuneer/VidChat %}