Writing a basic React frontend

The SignalWire Video SDK provides the video-feed of all participants in the video-call out of the box as a single stream. The video-feed is composited at SignalWire servers to ensure optimum use of bandwidth and scalability, which means that it isn't necessary to write complex code for layouting and composing the video stream. The SDK also provides an API to perform actions on the video feed, like muting videos and changing layout of the stream.

So we need to write our React code as a wrapper to the SDK's video stream. React will provide the user interface surrounding the stream to ensure members can join, leave, and otherwise interact with the video call.

Setting up the video feed component with React

To get a basic video application working with the Video API, we just need do the following 3 things:

  1. Get the token to access the Video API with appropriate permissions

We will use Axios to query the /get_token endpoint that we built in the previous section.

let token = await axios.post(YOUR_SERVER_URL+"/get_token", {
      user_name: roomDetails.name,
      room_name: roomDetails.room,
      mod: roomDetails.mod,
   });
console.log(token.data);
token = token.data.token;
  1. Have an empty container element that the SDK can inject its stream to

React maintains a Virtual DOM of all elements in the web app that need to be reactive. It is usually a really useful part of React. But the SignalWire Video SDK needs to inject its own elements into the DOM, and it isn’t a good idea to let React tamper with that.

The best way to avoid conflicts is to let the SDK inject into an empty <div />, as React has no need to keep track of empty elements.

return (
   <div
     id="stream"
     style={{
       width,
     }}
   ></div>
 );
  1. Join the room and register events

Finally, we can use a one-time useEffect to inject the room into the empty div using the SDK. The SDK can be initialized with the ID of the empty stream container.

room = new SignalWire.Video.RoomSession({
  token,
  rootElement: document.getElementById('stream'), // an html element to display the video
  video: true,
})

Note that useEffect doesn’t accept asynchronous functions so we need to define and call a separate async function inside the hook:

useEffect(() => {
  setup_room();
  async function setup_room() {
      // Video SDK initialization here
  }
}, []);

Events are registered to the room with the idiomatic use of RoomSession.on(‘event_name’, ()=>{}) described in the Video SDK documentation. For now, we are only using these event handlers to log these events, but soon they will play an important role in keeping our React state in sync with the global state of video conference.

room.on("room.joined", async (e) => {
  thisMemberId.current = e.member_id;
  eventLogger("You have joined the room.");
});

The complete code for this component (<Video />) would look like this:

import React, { useEffect, useRef, useState } from "react";
import axios from "axios";
import * as SignalWire from "@signalwire/js";
 
export default function Video({
 onRoomInit = () => {},
 width = 400,
 joinDetails: roomDetails = {
   room: "signalwire",
   name: "JohnDoe",
   mod: true,
 },
 eventLogger = (msg) => {
   console.log("Event:", msg);
 },
}) {
 let thisMemberId = useRef(null);
 
 useEffect(() => {
   setup_room();
   async function setup_room() {
     let token, room;
     try {
       token = await axios.post("/get_token", {
         user_name: roomDetails.name,
         room_name: roomDetails.room,
         mod: roomDetails.mod,
       });
       token = token.data.token;
 
       try {
         room = new SignalWire.Video.RoomSession({
           token,
           rootElement: document.getElementById('stream'), // an html element to display the video
           video: true,
         })

         room.on("room.joined", async (e) => {
           thisMemberId.current = e.member_id;
           eventLogger("You have joined the room.");
         });
         room.on("room.updated", async (e) => {
           eventLogger("Room has been updated");
         });
         room.on("member.joined", async (e) => {
           eventLogger(e.member.name + " has joined the room.");
         });
         room.on("layout.changed", async (e) => {
           eventLogger("The layout was changed to", e.layout.name);
         });
         room.on("member.left", async (e) => {
           eventLogger("A member has left the room.");
         });
 
         await room.join();
 
         onRoomInit(room);
       } catch (error) {
         console.error("Something went wrong", error);
       }
     } catch (e) {
       console.log(e);
       alert("Error encountered. Please try again.");
     }
   }
 }, [roomDetails, eventLogger, onRoomInit]);


 return (
   <div
     id="stream"
     style={{
       width,
     }}
   ></div>
 );
}

Using the component

We will use this component that we have just created as the central component that displays the video feed. Other helper components will surround this and facilitate interactions with SignalWire.

For this basic example, let's use this component to create a basic page:

import Video from "./Video";

export default function App() {
  return <Video />;
}

The CodeSandbox demonstrates how the code above is used. Feel free to change the code around to see what it does.


Did this page help you?