<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: kompfner</title>
    <description>The latest articles on DEV Community by kompfner (@kompfner).</description>
    <link>https://dev.to/kompfner</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F456444%2F930f40af-6d41-42c3-b986-a0296f24e170.png</url>
      <title>DEV Community: kompfner</title>
      <link>https://dev.to/kompfner</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kompfner"/>
    <language>en</language>
    <item>
      <title>Build a video chat app in minutes with React and daily-js</title>
      <dc:creator>kompfner</dc:creator>
      <pubDate>Mon, 14 Sep 2020 22:13:52 +0000</pubDate>
      <link>https://dev.to/trydaily/build-a-video-chat-app-in-minutes-with-react-and-daily-js-481c</link>
      <guid>https://dev.to/trydaily/build-a-video-chat-app-in-minutes-with-react-and-daily-js-481c</guid>
      <description>&lt;p&gt;With video chat apps on the rise for, well, &lt;a href="https://www.statista.com/chart/21268/global-downloads-of-video-chat-apps-amid-covid-19-pandemic/" rel="noopener noreferrer"&gt;obvious reasons&lt;/a&gt;, it’s increasingly important to be able to add video call capabilities to apps and websites quickly. The more customizable those video calls can be, the better for building unique user experiences.&lt;/p&gt;

&lt;p&gt;This post walks you through how to build a custom video chat app with React and &lt;a href="https://docs.daily.co/docs/reference-docs" rel="noopener noreferrer"&gt;the Daily API&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkz93gozlchbr1clwsvhl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fkz93gozlchbr1clwsvhl.png" alt="Two people smiling in a video call in the React demo app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What we’ll build
&lt;/h2&gt;

&lt;p&gt;In our app, when a user clicks to start a call, the app will create a &lt;a href="https://docs.daily.co/reference#rooms" rel="noopener noreferrer"&gt;meeting room&lt;/a&gt;, pass the room’s URL to a new &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface#daily-call-object" rel="noopener noreferrer"&gt;Daily call object&lt;/a&gt;, and join the call. The call object is something that keeps track of important information about the meeting, like other participants (including their audio and video tracks) and the things they do on the call (e.g. muting their mic or leaving), and provides methods for interacting with the meeting. The app leverages this object to update its state accordingly, and to carry out user actions like muting or screen-sharing. When the user leaves the meeting room, the call object is destroyed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you’ll need to build it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Daily account&lt;/strong&gt;: &lt;a href="https://dashboard.daily.co/signup" rel="noopener noreferrer"&gt;Sign up for an account&lt;/a&gt; if you don’t have one already.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloned &lt;code&gt;daily-demos/call-object-react&lt;/code&gt; Github repository&lt;/strong&gt;: The fastest way to follow along with this tutorial and get a demo app up and running is by &lt;a href="https://github.com/daily-demos/call-object-react" rel="noopener noreferrer"&gt;cloning this repo&lt;/a&gt;. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Familiarity with React&lt;/strong&gt;: In this post we skip over a lot of the code that isn’t related to Daily, so it could be worth brushing up on React and/or hooks [0]. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once you have those things, we’re ready to get started! &lt;/p&gt;

&lt;h2&gt;
  
  
  Build and run the app
&lt;/h2&gt;

&lt;p&gt;After you’ve &lt;a href="https://github.com/daily-demos/call-object-react" rel="noopener noreferrer"&gt;cloned the repository&lt;/a&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

cd call-object-react
npm i
npm run dev


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Now, open your browser and head to &lt;code&gt;localhost:&amp;lt;port&amp;gt;&lt;/code&gt;, using the port printed in the terminal after running the above. You should have an app running locally that mirrors the &lt;a href="https://call-object-react.netlify.app/" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;: you can click to start a call, and share the link with another participant or yourself in a new tab. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/BoBOKNtlR8rTi/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/BoBOKNtlR8rTi/giphy.gif" alt="Anakin Skywalker in Phantom Menace exclaims it’s working"&gt;&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;It’s great that it’s working...but &lt;em&gt;how&lt;/em&gt; is it working? &lt;/p&gt;
&lt;h2&gt;
  
  
  How the app works
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Daily concepts
&lt;/h3&gt;

&lt;p&gt;Before diving into the code, let’s cover some Daily fundamentals. &lt;/p&gt;
&lt;h4&gt;
  
  
  The call object
&lt;/h4&gt;

&lt;p&gt;A Daily call object is like a direct line to the Daily API. It gives us the finest-grained control over a video call, letting us access its lowest level foundations, like participant video and audio tracks. Invoking &lt;code&gt;DailyIframe.createCallObject()&lt;/code&gt; creates a call object. Once created, we pass a meeting room URL to the call object to join a call. &lt;/p&gt;

&lt;p&gt;In addition to all of that, the call object keeps track of our call’s state, both meeting state and participant state. &lt;/p&gt;
&lt;h5&gt;
  
  
  State #1: meeting state
&lt;/h5&gt;

&lt;p&gt;Meeting state tracks where a current (often called "local") participant is in the life of a call. A participant can start to join a call, be in a call, have left a call, or be experiencing an error. &lt;/p&gt;

&lt;p&gt;We can check a call’s meeting state via the call object with &lt;code&gt;callObject.meetingState()&lt;/code&gt;. If a participant is joining a meeting, the "joining-meeting" event will be returned, for example. &lt;/p&gt;

&lt;p&gt;Meeting state changes trigger events like "joining-meeting." We can add event listeners for those state changes with &lt;code&gt;callObject.on()&lt;/code&gt;. &lt;/p&gt;
&lt;h5&gt;
  
  
  State #2: participant state
&lt;/h5&gt;

&lt;p&gt;Participant state monitors  &lt;em&gt;all&lt;/em&gt; participants on the call (including the current user) and the video, audio, or other media they’re sharing with everybody else. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;callObject.participants()&lt;/code&gt; returns a set of participant objects, keyed by an ID (or "local", for the current user). Each participant object includes fields like &lt;code&gt;tracks&lt;/code&gt;, including the participant's raw audio and video tracks and their playable states.&lt;/p&gt;

&lt;p&gt;The events "participant-joined", "participant-left", and "participant-updated" broadcast changes to participant state. The first two are only sent when participants &lt;em&gt;other&lt;/em&gt; than the current local participant join or leave, while the latter fires on changes like camera and microphone toggling for any participant, including local. &lt;/p&gt;

&lt;p&gt;Now that we’ve covered the Daily call object and its states, we’re ready to look at our app. &lt;/p&gt;
&lt;h3&gt;
  
  
  What’s happening in the code
&lt;/h3&gt;
&lt;h3&gt;
  
  
  App.js: Creating, joining, and leaving a call
&lt;/h3&gt;

&lt;p&gt;Before we get into the details of each stage of a meeting, let’s look at how we wire up our top-level event listeners. In App.js, we listen for changes to &lt;code&gt;callObject.meetingState()&lt;/code&gt;, so we can update the UI for the local participant depending on where they’re at in their user journey: in a call, out of a call, or experiencing errors: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;



&lt;p&gt;When a local participant leaves a meeting, we call &lt;code&gt;callObject.destroy()&lt;/code&gt;. We do this to clean up our call object's global footprint, to open the door for our app to create another call object in the future with different create-time options.    &lt;/p&gt;

&lt;h4&gt;
  
  
  Creating a call
&lt;/h4&gt;

&lt;p&gt;When a participant clicks to start a call, they invoke the &lt;code&gt;createCall()&lt;/code&gt; function to create a short-lived, demo-only room. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;In real production code you'll want to create rooms by calling the &lt;a href="https://docs.daily.co/reference#your-dailyco-domain" rel="noopener noreferrer"&gt;Daily REST API&lt;/a&gt; from your backend server, to avoid storing API keys in your client-side JavaScript [1].&lt;/p&gt;

&lt;h4&gt;
  
  
  Joining a call
&lt;/h4&gt;

&lt;p&gt;Once we have a room, we’ll join it by invoking the &lt;a href="https://docs.daily.co/reference#%EF%B8%8F-join" rel="noopener noreferrer"&gt;&lt;code&gt;.join()&lt;/code&gt; method&lt;/a&gt; on the call object [2]. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;h4&gt;
  
  
  Leaving a call
&lt;/h4&gt;

&lt;p&gt;When a participant clicks the “Leave” button, we’ll initiate that process by invoking the &lt;a href="https://docs.daily.co/reference#%EF%B8%8F-leave" rel="noopener noreferrer"&gt;&lt;code&gt;leave()&lt;/code&gt; method&lt;/a&gt; on the call object [3, 4]. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;h3&gt;
  
  
  Call.js and callState.js: Using state to determine the call display
&lt;/h3&gt;

&lt;p&gt;We now know how different operations in a call take place, so the next step is to know how those operations affect our display. This involves keeping tabs on participant state in order to display the call's participants and their video and audio tracks.&lt;/p&gt;

&lt;p&gt;While App.js listened to &lt;code&gt;callObject.meetingState()&lt;/code&gt;, in Call.js we’ll listen for &lt;code&gt;callObject.participantState()&lt;/code&gt; and update our component state accordingly [5].&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Our demo app displays each participant (including the current user) as their own "tile", and also displays any screen share as its own tile independent of the participant doing the sharing.&lt;/p&gt;

&lt;p&gt;To accomplish this, we map &lt;code&gt;callObject.participantState()&lt;/code&gt; to the call’s component state, specifically into a set of "call items" in callState.js: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Each call item corresponds to a call participant, storing the participant’s video track, audio track, and a boolean that notes whether or not a participant is in the process of joining a call [6]. &lt;/p&gt;

&lt;p&gt;To populate the call items, we call our &lt;code&gt;getCallItems()&lt;/code&gt; function, which loops over participant state: &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;We import callState in Call.js, where we invoke the &lt;code&gt;getTiles()&lt;/code&gt; function to pass participant video and audio tracks to their respective tile components. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Now let’s take a closer look at those tiles. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l1J9K8V2MgXVX4vug/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l1J9K8V2MgXVX4vug/giphy.gif" alt="Pink and yellow tiles shift colors"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Tile.js: displaying each participant’s video stream
&lt;/h3&gt;

&lt;p&gt;Each of our tile components contains either a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; and/or an &lt;code&gt;&amp;lt;audio&amp;gt;&lt;/code&gt; element. Each tag references its respective DOM element [7]. Note the &lt;code&gt;autoPlay muted playsInline&lt;/code&gt; attribute. These are the set of attributes that will let your audio and video play automatically on Chrome, Safari, and Firefox.&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;Next up: give participants control over whether or not they display their videos and share their audio or screens. &lt;/p&gt;

&lt;h3&gt;
  
  
  Tray.js: Enable participant controls
&lt;/h3&gt;

&lt;p&gt;Once again we'll use participant state to determine whether we're actively sharing audio, video, and our screen.&lt;/p&gt;

&lt;p&gt;We’ll look specifically at &lt;code&gt;callObject.participants().local&lt;/code&gt;, since we’re concerned about adjusting the user interface for the current, or local, user. The only event we need to listen to is "participant-updated" [8]. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;p&gt;With our event listener handling state updates, we can wire up our buttons to handle the relevant &lt;code&gt;callObject&lt;/code&gt; methods to control user input: &lt;code&gt;.setLocalVideo&lt;/code&gt;, &lt;code&gt;.setLocalAudio&lt;/code&gt;, and &lt;code&gt;.startScreenShare&lt;/code&gt; or &lt;code&gt;.stopScreenShare&lt;/code&gt;. &lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;
 

&lt;h2&gt;
  
  
  What to add next
&lt;/h2&gt;

&lt;p&gt;Congratulations! If you’ve read this far, you now have an overview of your custom video chat app. To dig even deeper into the code, have a look at how the demo handles edge cases over &lt;a href="https://www.daily.co/blog/building-a-custom-video-chat-app-with-react/" rel="noopener noreferrer"&gt;on the Daily blog&lt;/a&gt;. Or, dive into our &lt;a href="https://github.com/daily-demos/call-object-react" rel="noopener noreferrer"&gt;demo repository&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/kimberleejohnson/daily-demos/tree/master/react-demo" rel="noopener noreferrer"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fai2gn6g0011b7eqwu5zg.png" alt="Button"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To see everything else the Daily APIs have to offer, grab a cup of tea and head over to &lt;a href="https://docs.daily.co/docs" rel="noopener noreferrer"&gt;docs.daily.co&lt;/a&gt; for some fun evening reading.&lt;/p&gt;

&lt;p&gt;Thanks for reading! As always, we'd love to know what you think and how we can better help you to build that next great video chat app, so please don't hesitate to &lt;a href="https://www.daily.co/contact-us" rel="noopener noreferrer"&gt;reach out&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/icVkqVBTfuBDczxyBH/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/icVkqVBTfuBDczxyBH/giphy.gif" alt="Dinaosaur in a cup that has a tea rex tea bag"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Footnotes
&lt;/h3&gt;

&lt;p&gt;[0] If you’d like to get familiar with React and come back later, check out the many great resources on DEV (like &lt;a href="https://dev.to/aspittel/a-complete-beginners-guide-to-react-2cl6"&gt;Ali Spittel’s intro&lt;/a&gt;), or the React docs for more on &lt;a href="https://reactjs.org/docs/hooks-intro.html" rel="noopener noreferrer"&gt;hooks&lt;/a&gt;. &lt;br&gt;
[1] Our teammate wrote an excellent post on &lt;a href="https://www.daily.co/blog/deploy-a-daily-co-backend-node-js-server-instantly/" rel="noopener noreferrer"&gt;how to set up an instant Daily server on  Glitch&lt;/a&gt;.&lt;br&gt;
[2] Note that, because we invoke &lt;code&gt;destroy()&lt;/code&gt; on our call object after each call ends, we need to create a new call object in order to join a room. This is not strictly necessary — you could hold onto a single call object for the lifetime of your app if you so desired, but, as we mentioned earlier, we prefer this approach to leave the door open for a future differently-configured call object.&lt;br&gt;
[3] You might’ve noticed that both the &lt;code&gt;join()&lt;/code&gt; and &lt;code&gt;leave()&lt;/code&gt; call object operations are asynchronous, as is &lt;code&gt;destroy()&lt;/code&gt;. To avoid undefined behavior and app errors, like leaving and destroying a call object simultaneously, it's important to prevent triggering one call object operation while another is pending. A straightforward way to do this is to use meeting state to update relevant buttons' idle states so that the user can't start an operation until it's safe, like we do in &lt;a href="https://github.com/daily-demos/call-object-react/blob/8564d4a6dd1b858e9f5eeab98231f3ebed81ecb6/src/components/App/App.js#L188" rel="noopener noreferrer"&gt;our demo app&lt;/a&gt;.&lt;br&gt;
[4] Because &lt;code&gt;destroy()&lt;/code&gt; is asynchronous, the DailyIframe.createCallObject() must only be invoked once destroy()'s Promise has been resolved.&lt;br&gt;
[5] In the demo app, we use a reducer to update the component state.&lt;br&gt;
[6] We only set &lt;code&gt;isLoading&lt;/code&gt; to true if we’ve &lt;em&gt;never&lt;/em&gt; received audio or video tracks for a participant. &lt;br&gt;
[7] We did that so we could programmatically set their &lt;code&gt;srcObject&lt;/code&gt; properties whenever our media tracks change (see &lt;a href="https://github.com/daily-demos/call-object-react/blob/8564d4a6dd1b858e9f5eeab98231f3ebed81ecb6/src/components/Tile/Tile.js#L18" rel="noopener noreferrer"&gt;lines 18-31 in Tile.js&lt;/a&gt;).&lt;br&gt;
[8] You might remember that "participant-joined" and "participant-left" are only ever about other (not local) participants.&lt;/p&gt;

</description>
      <category>react</category>
      <category>video</category>
      <category>daily</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
