<?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: Kimberlee Johnson </title>
    <description>The latest articles on DEV Community by Kimberlee Johnson  (@kimberleejohnson).</description>
    <link>https://dev.to/kimberleejohnson</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%2F165022%2Fe4a63140-22dd-4916-99c0-da647a7e856e.jpeg</url>
      <title>DEV Community: Kimberlee Johnson </title>
      <link>https://dev.to/kimberleejohnson</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kimberleejohnson"/>
    <language>en</language>
    <item>
      <title>What changelogs do you follow, and why? </title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Wed, 19 Jan 2022 21:18:51 +0000</pubDate>
      <link>https://dev.to/kimberleejohnson/what-changelogs-do-you-follow-and-why-2am6</link>
      <guid>https://dev.to/kimberleejohnson/what-changelogs-do-you-follow-and-why-2am6</guid>
      <description></description>
      <category>discuss</category>
    </item>
    <item>
      <title>Add flying emoji reactions to a React video chat app</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Thu, 02 Dec 2021 17:26:38 +0000</pubDate>
      <link>https://dev.to/trydaily/add-flying-emoji-reactions-to-a-react-video-chat-app-5hma</link>
      <guid>https://dev.to/trydaily/add-flying-emoji-reactions-to-a-react-video-chat-app-5hma</guid>
      <description>&lt;p&gt;If a picture is worth a thousand words, what does that mean for emoji? For decades they’ve been used to add color to all sorts of written communications, from text messages to entire translations of &lt;a href="http://emojidick.com/" rel="noopener noreferrer"&gt;Moby Dick&lt;/a&gt; to — most relevant to this blog post — video calls. &lt;/p&gt;

&lt;p&gt;We build developer tools at &lt;a href="https://www.daily.co/?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;Daily&lt;/a&gt; that enable new ways to communicate online. Adding emoji reactions to video chats gives participants a familiar (and fun!) way to express themselves.&lt;/p&gt;

&lt;p&gt;In this tutorial we’ll add a set of flying emoji reactions to a custom video call built on the Daily &lt;a href="https://docs.daily.co/call-object?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;call object&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%2Fuploads%2Farticles%2Frc76n3m5q6h3t9mqrgwf.gif" 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%2Fuploads%2Farticles%2Frc76n3m5q6h3t9mqrgwf.gif" alt="Participant on video call clicks star icon and then a Squid that flies across screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You might’ve seen similar emoji overlays in Instagram Live broadcasts, Twitter Periscope live streams, or Twitch “emote walls” that fill an entire screen on a live stream, for example. We’ll be making a similar wall of reaction emoji for our WebRTC video calls with a little React, CSS, and some Daily methods. &lt;/p&gt;

&lt;p&gt;To achieve this we will: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a button that sends an emoji of our choice flying on click &lt;/li&gt;
&lt;li&gt;Send our emoji reaction to all other participants using the Daily &lt;a href="https://docs.daily.co/reference/daily-js/instance-methods/send-app-message?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;&lt;code&gt;sendAppMessage()&lt;/code&gt;&lt;/a&gt; method&lt;/li&gt;
&lt;li&gt;Render the emoji for both the local participant who sent it and the remote participants who receive it &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We will do all of these things  in a Next.js demo app that we built in a previous &lt;a href="https://www.daily.co/blog/build-a-real-time-video-chat-app-with-next-js-and-daily/?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;post&lt;/a&gt;. Reference that tutorial for details on the foundation of the app, like participant, device, and track management. This post just focuses on the emoji 😎&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The demo and code snippets are React-based, but you can still apply the essence of the steps outlined in the tutorial to work with other frameworks.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;To run the demo locally: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork and clone the &lt;code&gt;daily-demos/examples&lt;/code&gt; &lt;a href="https://github.com/daily-demos/examples/tree/main/custom?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;repository&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cd examples/custom/flying-emojis&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Set your &lt;code&gt;DAILY_API_KEY&lt;/code&gt; and &lt;code&gt;DAILY_DOMAIN&lt;/code&gt; env variables (see &lt;code&gt;env.example&lt;/code&gt;) &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn workspace @custom/flying-emojis dev&lt;/code&gt; &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With that, our emoji are ready to fly. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/bcZ8T9ctIriAU/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/bcZ8T9ctIriAU/giphy.gif" alt="Witch waves at flying monkeys and says fly my pretties fly"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a button that sends an emoji flying
&lt;/h2&gt;

&lt;p&gt;The star icon, labeled "Emoji" in the call tray component, (&lt;a href="https://github.com/daily-demos/examples/blob/main/custom/flying-emojis/components/Tray.js?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;&lt;code&gt;Tray.js&lt;/code&gt;&lt;/a&gt;), reveals available emoji reactions, and allows participants to pick one to send. &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%2Fuploads%2Farticles%2Fa6ektjuqdn1ub4iwccra.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%2Fuploads%2Farticles%2Fa6ektjuqdn1ub4iwccra.png" alt="Icons at the bottom of a video call for call controls, Star icon on click reveals three emoji"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s that component’s structure, with tangential elements removed:&lt;/p&gt;

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

&lt;span class="c1"&gt;// Tray.js &lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;showEmojis&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emojis&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fire&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="err"&gt;🔥&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;squid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="err"&gt;🦑&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt;
        &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sendEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;laugh&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="err"&gt;🤣&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TrayButton&lt;/span&gt;
    &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Emoji&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setShowEmojis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;showEmojis&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
  &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;IconStar&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TrayButton&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;

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

&lt;/div&gt;

&lt;p&gt;When the star icon is clicked, it displays the available emoji. When a participant selects an emoji, the component calls &lt;code&gt;sendEmoji()&lt;/code&gt; and passes a string representing the selection. For example, after clicking on "🦑" &lt;code&gt;onClick={() =&amp;gt; sendEmoji('squid')}&lt;/code&gt; is called.&lt;/p&gt;

&lt;p&gt;Let’s look at &lt;code&gt;sendEmoji()&lt;/code&gt;. &lt;/p&gt;

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

&lt;span class="c1"&gt;// Tray.js &lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;sendEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatchEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;CustomEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;reaction_added&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="nf"&gt;setShowEmojis&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;sendEmoji()&lt;/code&gt; triggers a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent" rel="noopener noreferrer"&gt;&lt;code&gt;CustomEvent&lt;/code&gt;&lt;/a&gt; that we named &lt;code&gt;reaction_added&lt;/code&gt;. The string representing the emoji is &lt;code&gt;reaction_added&lt;/code&gt;’s &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail" rel="noopener noreferrer"&gt;&lt;code&gt;CustomEvent.detail&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We’ll listen for the &lt;code&gt;reaction_added&lt;/code&gt; event in &lt;code&gt;FlyingEmojisOverlay.js&lt;/code&gt;, via &lt;code&gt;window.addEventListener('reaction_added', handleSendFlyingEmoji);&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Use &lt;code&gt;sendAppMessage()&lt;/code&gt; to broadcast the emoji to other call participants
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;handleSendFlyingEmoji()&lt;/code&gt; gets the string representing the emoji from &lt;code&gt;CustomEvent.detail&lt;/code&gt;, and broadcasts it to all other call participants using the Daily &lt;a href="https://docs.daily.co/reference/daily-js/instance-methods/send-app-message?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;&lt;code&gt;sendAppMessage()&lt;/code&gt;&lt;/a&gt; method:&lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojiOverlay.js&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleSendFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;emoji&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendAppMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;handleDisplayFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;sendAppMessage()&lt;/code&gt; emits a corresponding &lt;a href="https://docs.daily.co/reference/daily-js/events/participant-events#app-message?utm_source=dev&amp;amp;utm_campaign=dev-flying-emojis" rel="noopener noreferrer"&gt;&lt;code&gt;app-message&lt;/code&gt;&lt;/a&gt; event that all remote participants receive. The &lt;code&gt;&amp;lt;FlyingEmojiOverlay /&amp;gt;&lt;/code&gt; component listens for the event and calls &lt;code&gt;handleReceiveFlyingEmoji()&lt;/code&gt; when a message is received: &lt;code&gt;callObject.on('app-message', handleReceiveFlyingEmoji);&lt;/code&gt;. &lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojisOverlay.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleReceiveFlyingEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nf"&gt;handleDisplayFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;handleDisplayFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;handleReceiveFlyingEmoji()&lt;/code&gt; passes the message data from &lt;code&gt;e.data.message&lt;/code&gt; along to &lt;code&gt;handleDisplayFlyingEmoji()&lt;/code&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Render the emoji for both the local sender and the remote recipient
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;handleDisplayFlyingEmoji()&lt;/code&gt; is called both on sending, in &lt;code&gt;handleSendFlyingEmoji()&lt;/code&gt; and upon receiving in &lt;code&gt;handleReceiveFlyingEmoji()&lt;/code&gt;. That’s because &lt;code&gt;app-message&lt;/code&gt; only fires for remote participants, but we want the local participant to see their own emoji reaction as well. &lt;/p&gt;

&lt;p&gt;The &lt;code&gt;handleDisplayFlyingEmoji()&lt;/code&gt; function takes a string as a parameter. &lt;code&gt;handleSendFlyingEmoji()&lt;/code&gt; passes the display handler a string from the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/detail" rel="noopener noreferrer"&gt;&lt;code&gt;CustomEvent.detail&lt;/code&gt;&lt;/a&gt; from the window event, while &lt;code&gt;handleReceiveFlyingEmoji()&lt;/code&gt; passes a string from the &lt;code&gt;app-message&lt;/code&gt; event object, &lt;code&gt;e.data.message&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Now that we know how and when &lt;code&gt;handleDisplayFlyingEmoji()&lt;/code&gt; is executed, let’s have a look at its definition: &lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojisOverlay.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleDisplayFlyingEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EMOJI_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emoji wiggle-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emoji wiggle-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`rotate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;animationend&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;handleRemoveFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;handleRemoveFlyingEmoji&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Let’s break it all down. &lt;/p&gt;

&lt;p&gt;First, it creates a new &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, and appends the selected emoji in a text node to that div. &lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojiOverlay.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;div&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createTextNode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;EMOJI_MAP&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;emoji&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;It gets the emoji by referencing a CONSTANT &lt;code&gt;EMOJI_MAP&lt;/code&gt; object whose keys map to emoji: &lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojisOverlay.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;EMOJI_MAP&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;fire&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🔥&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;squid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🦑&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;laugh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;🤣&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Once the emoji is added, the function applies styles. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random" rel="noopener noreferrer"&gt;&lt;code&gt;Math.random()&lt;/code&gt;&lt;/a&gt; sets the &lt;code&gt;className&lt;/code&gt; to either &lt;code&gt;'emoji wiggle-1'&lt;/code&gt; or &lt;code&gt;'emoji wiggle-2'&lt;/code&gt;. &lt;/p&gt;

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

&lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="nt"&gt;FlyingEmojisOverlay&lt;/span&gt;&lt;span class="nc"&gt;.js&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;wiggle-1&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;@keyframes&lt;/span&gt; &lt;span class="n"&gt;wiggle-2&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;from&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nt"&gt;to&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;margin-left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;-50px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;These classes determine where the emoji starts wiggling on the screen. &lt;code&gt;Math.random()&lt;/code&gt; also determines the degree to which the emoji rotates, and its &lt;code&gt;left&lt;/code&gt; position. &lt;/p&gt;

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

 &lt;span class="c1"&gt;// FlyingEmojiOverlay.js &lt;/span&gt;

&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
       &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emoji wiggle-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;emoji wiggle-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`rotate(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;deg)`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;style&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;%`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;With styling set, the emoji is ready to be added to &lt;code&gt;overlayRef&lt;/code&gt;: &lt;/p&gt;

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

&lt;span class="c1"&gt;// FlyingEmojisOverlay.js&lt;/span&gt;

&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;


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

&lt;/div&gt;

&lt;p&gt;Finally, &lt;code&gt;handleDisplayFlyingEmoji()&lt;/code&gt; listens for the emoji animation to end, &lt;code&gt;node.addEventListener('animationend', (e) =&amp;gt; handleRemoveFlyingEmoji(e.target));&lt;/code&gt; and then removes the appended child: &lt;/p&gt;


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

&lt;p&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleRemoveFlyingEmoji&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;br&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;br&gt;
   &lt;span class="nx"&gt;overlayRef&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;removeChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;node&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;br&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;&lt;/p&gt;

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

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  What’s next ❓&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;We hope this tutorial has helped you add personality to your video calls. To build on this &lt;a href="https://github.com/daily-demos/examples/tree/main/custom/flying-emojis" rel="noopener noreferrer"&gt;demo&lt;/a&gt;, you could: experiment with emoji that multiply and burst more quickly in a “particle effect” (instead of a gentle float, maybe they bounce around the video window); generate random emoji; add reactions to a &lt;a href="https://www.daily.co/blog/tag/webinar/" rel="noopener noreferrer"&gt;webinar&lt;/a&gt; app, or explore libraries like &lt;a href="https://github.com/loonywizard/js-confetti" rel="noopener noreferrer"&gt;confetti&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To keep reading for more inspiration, Butter, a web events facilitation platform, has a writeup on on their engineering blog on how they implemented &lt;a href="https://eng.butter.us/awesome-floating-emoji-reactions-using-framer-motion-styled-components-and-lottie-36b9f479a9f9" rel="noopener noreferrer"&gt;floating emojis&lt;/a&gt; for their video chats with &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer Motion&lt;/a&gt; and &lt;a href="https://github.com/airbnb/lottie-web" rel="noopener noreferrer"&gt;Lottie Web&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The world is your oyster, 🌍🦪.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>react</category>
      <category>nextjs</category>
    </item>
    <item>
      <title>Add video chat to a Next.js app in 30 minutes with Daily Prebuilt</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Thu, 07 Oct 2021 19:52:08 +0000</pubDate>
      <link>https://dev.to/trydaily/add-video-chat-to-a-next-js-app-in-30-minutes-with-daily-prebuilt-48b9</link>
      <guid>https://dev.to/trydaily/add-video-chat-to-a-next-js-app-in-30-minutes-with-daily-prebuilt-48b9</guid>
      <description>&lt;p&gt;With the launch of our new &lt;a href="https://docs.daily.co/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;docs site&lt;/a&gt;, we’ve been spending a lot of time in &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;. We even got a little meta and embedded a &lt;a href="https://www.daily.co/prebuilt?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Daily Prebuilt&lt;/a&gt; demo, built on Next, into the docs site, also built on Next. &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%2Fuploads%2Farticles%2Fayr9oz0lz08vbe5tg16l.jpeg" 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%2Fuploads%2Farticles%2Fayr9oz0lz08vbe5tg16l.jpeg" alt="Three participants on a video call embedded within another web page prompting for video call test"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo lets readers quickly test out Daily calls, and get a sense of what Daily Prebuilt would look like embedded in their own app, right on the docs site. Our docs use Next &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;API routes&lt;/a&gt; to create temporary Daily rooms dynamically server-side. &lt;/p&gt;

&lt;p&gt;Since our docs codebase isn’t currently public, this post uses our &lt;a href="https://github.com/daily-demos/examples/tree/main/prebuilt/basic-embed?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;&lt;code&gt;/examples/prebuilt/basic-embed&lt;/code&gt; repository&lt;/a&gt; as a template to show how you can do the same in any Next app. We’ll cover: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up the repository locally &lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;Next API routes&lt;/a&gt; to create Daily rooms dynamically server-side &lt;/li&gt;
&lt;li&gt;Creating a Daily callframe and joining a call once we have a room&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You’ll need a &lt;a href="https://dashboard.daily.co/signup?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Daily account&lt;/a&gt; if you don’t have one already. &lt;/p&gt;

&lt;p&gt;Skip to API routes if you already have a Next project that you want to add video chat to, or if you’d rather run &lt;a href="https://nextjs.org/docs/api-reference/create-next-app" rel="noopener noreferrer"&gt;&lt;code&gt;create-next-app&lt;/code&gt;&lt;/a&gt; to start a new app from scratch. &lt;/p&gt;

&lt;h2&gt;
  
  
  Set up the demo repository
&lt;/h2&gt;

&lt;p&gt;Clone the &lt;a href="https://github.com/daily-demos/examples?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;&lt;code&gt;/examples&lt;/code&gt; repo&lt;/a&gt;, and &lt;code&gt;cd examples/prebuilt/basic-embed&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;.env&lt;/code&gt; based on &lt;code&gt;.env.example&lt;/code&gt;, adding your &lt;a href="https://docs.daily.co/reference/rest-api/your-domain?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Daily domain&lt;/a&gt; (you set this up when you created an account) and API key (you can find this in the "Developers" tab in the &lt;a href="https://dashboard.daily.co/developers?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Daily dashboard&lt;/a&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DAILY_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"your-domain"&lt;/span&gt;
&lt;span class="nv"&gt;DAILY_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Daily API Key"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you’ve added your own values, run the following from inside &lt;code&gt;/basic-embed&lt;/code&gt;  to install dependencies and start the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn 
yarn workspace @prebuilt/basic-embed dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be able to click "Create room and start" and jump into a Daily Prebuilt call: &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%2Fuploads%2Farticles%2F4ij2u3cztfamrbb206e3.gif" 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%2Fuploads%2Farticles%2F4ij2u3cztfamrbb206e3.gif" alt="Clicking on create room and start on a web page starts a call with one video participant"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let’s look at how that all works. &lt;/p&gt;

&lt;h2&gt;
  
  
  Use API routes to create Daily video rooms dynamically server-side
&lt;/h2&gt;

&lt;p&gt;Our &lt;a href="https://github.com/daily-demos/examples/tree/main/prebuilt/basic-embed/pages?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;&lt;code&gt;/pages&lt;/code&gt; directory&lt;/a&gt; is where most of the fun happens. &lt;a href="https://nextjs.org/docs/basic-features/pages" rel="noopener noreferrer"&gt;Next pages&lt;/a&gt; are React components. They’re associated with routes based on their file names, and come with some other neat built-in features. &lt;/p&gt;

&lt;p&gt;For example, files inside &lt;code&gt;pages/api&lt;/code&gt; are treated like API endpoints. These are &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;Next API routes&lt;/a&gt;. In development, they are served by your dev servers, but in prod in our demo app they’ll get converted into Vercel functions, technically making them &lt;a href="https://nextjs.org/docs/deployment#optimized-for-nextjs" rel="noopener noreferrer"&gt;serverless&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%2Fuploads%2Farticles%2F69jp5z82wdwga5m7r87b.gif" 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%2Fuploads%2Farticles%2F69jp5z82wdwga5m7r87b.gif" alt="Football coach from Ted Lasso Coach Beard raises hands over head excitedly like mind is blown"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In our app, we use a Next API route to create Daily rooms:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/room/index.js &lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAILY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;enable_prejoin_ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;enable_network_ui&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;enable_screenshare&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;enable_chat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;round&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;eject_at_room_exp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;}),&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dailyRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAILY_REST_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/rooms`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;options&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dailyRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All requests to &lt;code&gt;/room&lt;/code&gt; are handled here, and we’re specifically adding a case to handle a POST request. The request references both the Daily API key and base REST domain in the .env.&lt;/p&gt;

&lt;p&gt;We send this request in the &lt;code&gt;&amp;lt;CreateRoomButton /&amp;gt;&lt;/code&gt; component. This component is a button that onClick creates a room:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/CreateRoom.js&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createRoom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isValidRoom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nx"&gt;Create&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt; &lt;span class="nx"&gt;and&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createRoom()&lt;/code&gt; sends a request to the Next &lt;code&gt;/api/room&lt;/code&gt; endpoint, which makes the Daily endpoint POST request in &lt;a href="https://github.com/daily-demos/examples/blob/main/prebuilt/basic-embed/pages/index.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;&lt;code&gt;api/room/index&lt;/code&gt;&lt;/a&gt; described above:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/CreateRoom.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createRoom&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/room&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="c1"&gt;// Abridged snippet &lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When that request resolves, it returns the Daily response object, including a &lt;code&gt;url&lt;/code&gt; value. &lt;code&gt;createRoom()&lt;/code&gt; sets the &lt;code&gt;room&lt;/code&gt; value stored in local state to the response object’s &lt;code&gt;url&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/CreateRoom.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;setRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have a room, we’re ready for a callframe. &lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Daily callframe and join a call
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;&amp;lt;Call /&amp;gt;&lt;/code&gt; component not only renders &lt;code&gt;&amp;lt;CreateRoom /&amp;gt;&lt;/code&gt;, but also initializes the callframe with a &lt;code&gt;useEffect&lt;/code&gt; hook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Call.js&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="nf"&gt;createAndJoinCall&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createAndJoinCall&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The hook calls &lt;code&gt;createAndJoinCall()&lt;/code&gt;, a function that: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a new Daily callframe, embedding it in the ref we identified &lt;code&gt;&amp;lt;div ref={callRef} className="call" /&amp;gt;&lt;/code&gt; and passing along some &lt;a href="https://docs.daily.co/reference/daily-js/daily-iframe-class/properties#properties?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;properties&lt;/a&gt; we stored in the &lt;code&gt;CALL_OPTIONS&lt;/code&gt; constant &lt;/li&gt;
&lt;li&gt;Joins the Daily room using the &lt;code&gt;room&lt;/code&gt; value stored in local state 

&lt;ul&gt;
&lt;li&gt;Listens for the &lt;a href="https://docs.daily.co/reference/daily-js/events/meeting-events#left-meeting?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;&lt;code&gt;'left-meeting'&lt;/code&gt;&lt;/a&gt; event so it can reset app state when the local participant leaves the call
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Call.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;createAndJoinCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newCallFrame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;DailyIframe&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;callRef&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;CALL_OPTIONS&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nf"&gt;setCallFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newCallFrame&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="nx"&gt;newCallFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;leaveCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setCallFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nx"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="nx"&gt;newCallFrame&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;left-meeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leaveCall&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCallFrame&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createAndJoinCall()&lt;/code&gt; is invoked whether a room is created dynamically in real-time, as we walked through in the &lt;code&gt;&amp;lt;CreateRoom /&amp;gt;&lt;/code&gt; component, or a room is submitted through the input rendered in &lt;code&gt;&amp;lt;Home /&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Home.js &lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Field&lt;/span&gt; &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Or enter room to join&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TextInput&lt;/span&gt;
        &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomRef&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;placeholder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Enter room URL...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^(https:&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;/)?[&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;w.-]+(&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;.(daily&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;.(co)))+[&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;/]+[&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s2"&gt;w.-]+$&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;checkValidity&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Field&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The input calls &lt;code&gt;checkValidity()&lt;/code&gt; as its values change. This function makes sure that the entered text is a valid Daily room URL based on the &lt;code&gt;pattern&lt;/code&gt; value, and sets the local state value &lt;code&gt;isValidRoom&lt;/code&gt; to &lt;code&gt;true&lt;/code&gt; if so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Home.js &lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;checkValidity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;checkValidity&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setIsValidRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;isValidRoom&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This enables the "Join room" button:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Home.js &lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;joinCall&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isValidRoom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="nx"&gt;Join&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clicking the button calls  &lt;code&gt;joinCall()&lt;/code&gt;, which sets the &lt;code&gt;room&lt;/code&gt; value stored in local state to the input:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// components/Home.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;joinCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roomUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;roomRef&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;current&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nf"&gt;setRoom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;roomUrl&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;roomRef&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;room&lt;/code&gt; value in local state triggers the callframe creation in &lt;code&gt;&amp;lt;Call /&amp;gt;&lt;/code&gt; in the same way it did when we created a room dynamically. In both cases a &lt;code&gt;room&lt;/code&gt; value also instructs &lt;code&gt;index.js&lt;/code&gt; to display the &lt;code&gt;&amp;lt;Call /&amp;gt;&lt;/code&gt; instead of the &lt;code&gt;&amp;lt;Home /&amp;gt;&lt;/code&gt; component, according to this ternary statement:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.js      &lt;/span&gt;

&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Call&lt;/span&gt;
           &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;expiry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;expiry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;setRoom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setRoom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;setCallFrame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setCallFrame&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;callFrame&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;callFrame&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;
           &lt;span class="nx"&gt;setRoom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setRoom&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;setExpiry&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;setExpiry&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nx"&gt;isConfigured&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isConfigured&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="p"&gt;)}&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  thank u, Next.js
&lt;/h2&gt;

&lt;p&gt;That’s the core of the app! There are a few other tangential things in the codebase that we didn’t get into, like the &lt;code&gt;&amp;lt;ExpiryTimer /&lt;/code&gt;&amp;gt;&lt;code&gt;component and how we put [&lt;/code&gt;getStaticProps()`](&lt;a href="https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation" rel="noopener noreferrer"&gt;https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation&lt;/a&gt;) to work checking for env variables, but we welcome you to explore those things yourself and ping us with questions. Or, if you’d rather build your own video chat interface with Next.js, check out our post using &lt;a href="https://www.daily.co/blog/build-a-real-time-video-chat-app-with-next-js-and-daily/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Next with the Daily call object&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  More resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer"&gt;Next docs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.daily.co/prebuilt?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily-prebuilt" rel="noopener noreferrer"&gt;Daily Prebuilt guide&lt;/a&gt; &lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>video</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Did Dolly Parton’s ice cream really break the internet? </title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Mon, 21 Jun 2021 04:36:09 +0000</pubDate>
      <link>https://dev.to/kimberleejohnson/did-dolly-parton-s-ice-cream-really-break-the-internet-nef</link>
      <guid>https://dev.to/kimberleejohnson/did-dolly-parton-s-ice-cream-really-break-the-internet-nef</guid>
      <description>&lt;p&gt;Like many people, I’m a Dolly Parton fan. I’ve been wearing a mask with her face on it all year (&lt;a href="https://twitter.com/kimeejohnson/status/1386013338702737411" rel="noopener noreferrer"&gt;thanks, Chloe!&lt;/a&gt;); I celebrated her birthday in January with &lt;a href="https://twitter.com/kimeejohnson/status/1351580794632892416" rel="noopener noreferrer"&gt;a Chrome DevTools script&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m also an &lt;a href="https://twitter.com/kimeejohnson/status/1316940713624588294" rel="noopener noreferrer"&gt;ice cream fan&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;There are lots of us who &lt;em&gt;really&lt;/em&gt; like both of these things! &lt;a href="https://jenis.com/" rel="noopener noreferrer"&gt;Jeni’s Splendid Ice Creams&lt;/a&gt; combined two crowd pleasers when they collaborated with Dolly on a limited edition Strawberry Pretzel Pie pint to benefit her &lt;a href="https://imaginationlibrary.com/" rel="noopener noreferrer"&gt;Imagination Library&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%2Fuploads%2Farticles%2F7j1t5n01wvyqj410atc6.jpeg" 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%2Fuploads%2Farticles%2F7j1t5n01wvyqj410atc6.jpeg" alt="Ice cream pint reads Strawberry Pretzel Pie"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When the pints went on sale online, Jeni's website crashed, fast: &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1380189252311474182-443" src="https://platform.twitter.com/embed/Tweet.html?id=1380189252311474182"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1380189252311474182-443');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1380189252311474182&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;This Jeni’s Splendid Schadenfreude inspired me to ask: What actually happens when a website crashes? My &lt;a href="https://www.daily.co/" rel="noopener noreferrer"&gt;Daily&lt;/a&gt; colleague &lt;a href="https://dev.to/cbhill127"&gt;Brian&lt;/a&gt; was kind enough to entertain my curiosity. Read on for what I learned from our conversation, including a review of what happens when you visit a URL in your browser, an intro to CDNs, and a few possible failure modes. To be clear, that last part will just be some Splendid Speculation. &lt;/p&gt;

&lt;h2&gt;
  
  
  We all scream for ice cream
&lt;/h2&gt;

&lt;p&gt;It’s a question almost as popular during tech interviews as Dolly is all the time: What happens when you visit a URL in your browser? &lt;/p&gt;

&lt;p&gt;I wrote about &lt;a href="https://dev.to/trydaily/what-happens-when-you-type-a-video-chat-url-in-your-browser-4bp0"&gt;what happens when you visit a video chat URL&lt;/a&gt; in your browser, and at the highest level it’s largely the same for any link. The browser starts the Domain Name System (DNS) lookup process, which matches the URL to the IP address of the server hosting the site. Assuming the lookup is successful, the browser initiates a TCP connection with that IP, and sends a request for the site’s resources. Back and forth negotiation ensues, and if all goes well, the requested resource is returned. &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%2Fuploads%2Farticles%2Fj4ghotqjl0u7oq7s5er0.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%2Fuploads%2Farticles%2Fj4ghotqjl0u7oq7s5er0.png" alt="Diagram of DNS lookup and following server request"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s a lot of back and forth for a single request. Multiply that by the number of people who scream for both Jeni’s ice cream and Dolly Parton, and very quickly there are a lot of requests and negotiations for a single server to handle.&lt;br&gt;&lt;br&gt;
Unlike novelty ice cream flavors, managing web traffic is not a new and limited edition problem. Content Delivery Networks (CDNs) have been around for years to help businesses manage it.  &lt;/p&gt;

&lt;h2&gt;
  
  
  CDN: a coat of many servers
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;(Okay, a CDN is really more like &lt;a href="https://www.cloudflare.com/learning/cdn/what-is-a-cdn/" rel="noopener noreferrer"&gt;a network of many servers&lt;/a&gt;, but I had to try to make &lt;a href="https://www.youtube.com/watch?v=LQjMCKq87N0&amp;amp;ab_channel=StephanKoningsStephanKonings" rel="noopener noreferrer"&gt;the song&lt;/a&gt; work).&lt;/em&gt; &lt;/p&gt;

&lt;p&gt;When a website uses a CDN, the CDN makes its resources, like images and JavaScript, available across the CDN’s server network. There are lots of advantages to this. If resources were instead concentrated at a single server location, then it matters where site visitors physically are. The farther away a visitor is from the server, the longer it would take for the server to return the resources they request from it. Websites get around this problem by using a CDN. The CDN is configured to know where to find all of the website’s resources, and the  website then points its DNS records at the CDN. Then, when a visitor looks up the website and their browser initiates the DNS process, it finds the addresses at the CDN, and the nearest server in the network returns the resource. &lt;/p&gt;

&lt;p&gt;Unfortunately, even with a CDN, lots of things can still go wrong. I was one of many Jeni's customers who saw this message from CDN provider &lt;a href="https://www.cloudflare.com/cdn/" rel="noopener noreferrer"&gt;Cloudflare&lt;/a&gt; when trying to get my hands on a pint of Dolly's ice cream.  &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%2Fuploads%2Farticles%2Fmvok06p4n7vvzjp5t723.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%2Fuploads%2Farticles%2Fmvok06p4n7vvzjp5t723.png" alt="Screenshot reads Error 504 message showing broken Jeni's server"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;While it’s impossible to know what exactly happened, we can think of a few things. &lt;/p&gt;

&lt;h2&gt;
  
  
  Failure modes: &lt;a href="https://www.youtube.com/watch?v=sOzNJ9quibc" rel="noopener noreferrer"&gt;the salt in my tears&lt;/a&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A “thundering herd” took down the host
&lt;/h3&gt;

&lt;p&gt;Like many hyped limited product releases, Jeni’s launched the Dolly pints for sale online at a specific time: 12pm ET on April 8, 2021. &lt;/p&gt;

&lt;p&gt;Many fans across the country flocked to the website on the dot, flooding the CDN with requests for resources. If the CDN servers all across the network don’t have the resources stored locally (stay tuned for more on caching configuration!), they’ll need to request them from the host site. All of those geographically distributed servers making the same request at the same time from the same host may have quickly overwhelmed it. &lt;/p&gt;

&lt;p&gt;Depending on different servers’ configurations across the network, some servers at some locations could be requesting different resources, or be closer to the host. That explains why some customers, though very few, were able to successfully buy ice cream, and why so many different people saw so many different screens. &lt;/p&gt;

&lt;h3&gt;
  
  
  CDN configuration conflicted with site traffic needs
&lt;/h3&gt;

&lt;p&gt;When a website sets up a CDN, it configures how long the CDN holds onto cached resources. The CDN can update resources every time a visitor requests them (generally leading to slower responses and not ideal!), or to only refresh a resource every X or so minutes, depending on how okay the host is with a page or image being stale when somebody visits the site. &lt;/p&gt;

&lt;p&gt;If lots of updates are happening, let’s say the number of available pints in a database is decreasing, but a CDN only checks for that update every X minutes, somebody making a request could be on the receiving end of stale data, and then unable to complete a checkout. &lt;/p&gt;

&lt;p&gt;The way CDN settings interact with host settings, especially under a large amount of traffic, can quickly get complicated and break things by sending too many requests. &lt;/p&gt;

&lt;h3&gt;
  
  
  Host-specific processes got overwhelmed
&lt;/h3&gt;

&lt;p&gt;While a CDN can cache static resources like images on a marketing page, it can’t actually help with some of the more interactive parts of a website that involve sharing unique customer information, like entering a credit card or billing address. It’s possible that the origin server could’ve been overwhelmed with people trying to actually complete purchases. CDNs improve overall capacity with lots and lots of servers in lots and lots of places, but CDN resource caching doesn’t necessarily help the website process individual purchases. The error screen I saw on &lt;code&gt;jenis.com&lt;/code&gt; did indeed look different from the error screen I saw on &lt;code&gt;shop.jenis.com&lt;/code&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%2Fuploads%2Farticles%2F51za5wj2sviny9qgebwe.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%2Fuploads%2Farticles%2F51za5wj2sviny9qgebwe.png" alt="Error message reads please try again"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;a href="https://www.youtube.com/watch?v=XjPmg0inMpw" rel="noopener noreferrer"&gt;It’s all wrong, but it’s all right&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Jeni’s specifically called out website traffic testing in their statement about the Dolly debacle: &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%2Fuploads%2Farticles%2Fgc8fqq50z55vg9yh4hkb.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%2Fuploads%2Farticles%2Fgc8fqq50z55vg9yh4hkb.png" alt="Arrow points out that the website had fifty times its typical level of traffic"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Of course, it’s always possible to make a mistake in preparedness, like just not having enough servers, etc. But, it’s also really, really hard to simulate a Dolly Parton level of website traffic! Predicting the exact requests to be made in the exact configuration is impossible to simulate to a 100% degree of accuracy. And, while there are lots of advanced website testing services today, nothing exactly replicates real requests from real customers in real time. It’s kind of like how Dolly herself lost a Dolly Parton &lt;a href="https://www.womansworld.com/posts/entertainment/dolly-parton-lookalike-164680" rel="noopener noreferrer"&gt;lookalike contest&lt;/a&gt;. Imitation and reality are not the same. &lt;/p&gt;

&lt;p&gt;Dolly’s ice cream didn’t break the internet. It just overloaded Jeni’s host server in some way, the details of which are impossible to pinpoint from the outside. But, like a country song, it gives us some things to think about and stories to tell. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/U4dU6h55ePVMoGeG5Y/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/U4dU6h55ePVMoGeG5Y/giphy.gif" alt="Dolly Parton singing"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>devops</category>
      <category>beginners</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Build a real time video chat app with Next.js and Daily</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Fri, 18 Jun 2021 21:50:46 +0000</pubDate>
      <link>https://dev.to/trydaily/build-a-real-time-video-chat-app-with-next-js-and-daily-1kl7</link>
      <guid>https://dev.to/trydaily/build-a-real-time-video-chat-app-with-next-js-and-daily-1kl7</guid>
      <description>&lt;p&gt;We built one of our first &lt;a href="https://github.com/daily-demos/call-object-react?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;Daily demos&lt;/a&gt; with &lt;a href="https://www.daily.co/blog/building-a-custom-video-chat-app-with-react/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;React&lt;/a&gt;, because we like working with the framework. We’re not alone. More developers expressed interest in learning React than in picking up any other web framework in the &lt;a href="https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-web-frameworks-wanted2" rel="noopener noreferrer"&gt;2020 Stack Overflow Developer Survey&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Meta frameworks for React like &lt;a href="https://nextjs.org/" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt; are also gaining traction, so we built a &lt;a href="https://github.com/daily-demos/examples/tree/main/dailyjs/basic-call?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;basic video call demo app&lt;/a&gt; using Next.js and the &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;Daily call object&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%2Fuploads%2Farticles%2F3gqpldn0ja5f12i9a0b2.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%2Fuploads%2Farticles%2F3gqpldn0ja5f12i9a0b2.png" alt="Screenshot of a video chat app"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo draws inspiration from the new &lt;a href="https://www.daily.co/blog/announcing-the-new-daily-prebuilt-for-developers/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;Daily Prebuilt&lt;/a&gt; (We’ll eventually open source Daily Prebuilt’s components, stay tuned!), using shared &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;contexts&lt;/a&gt; and &lt;a href="https://reactjs.org/docs/hooks-custom.html" rel="noopener noreferrer"&gt;custom hooks&lt;/a&gt; that we hope help get your own apps up and running ASAP. Dive right into &lt;a href="https://github.com/daily-demos/examples?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;the repository&lt;/a&gt; or read on for a sneak peek at some of the most foundational pieces, like the core call loop (shared contexts and hooks) and generating meeting tokens. &lt;/p&gt;

&lt;h2&gt;
  
  
  Run the demo locally
&lt;/h2&gt;

&lt;p&gt;You can find our basic Next.js and Daily video chat demo in our ✨ new ✨ &lt;a href="https://github.com/daily-demos/examples/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;daily-demos/examples&lt;/code&gt; repository&lt;/a&gt;. This is a living repo. It’ll grow and evolve as Daily does and as we receive feedback. Poke around and you might notice a few other demos in progress. To hop right into the basic Next.js and Daily app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Fork and clone the &lt;a href="https://github.com/daily-demos/examples/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;repository&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cd examples/dailyjs/basic-call&lt;/code&gt;&lt;/li&gt;
&lt;li&gt; Set your &lt;code&gt;DAILY_API_KEY&lt;/code&gt; and &lt;code&gt;DAILY_DOMAIN&lt;/code&gt; environment variables (see &lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/basic-call/env.example?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;env.example&lt;/a&gt;) &lt;/li&gt;
&lt;li&gt;&lt;code&gt;yarn&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;yarn workspace @dailyjs/basic-call dev&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  The core call loop: shared contexts and hooks
&lt;/h2&gt;

&lt;p&gt;As you’re probably well aware in the year 2021, lots of things can happen on video calls. Participants join and leave, mute and unmute their devices, not to mention the funny things networks can decide to do. Application state can get unwieldy quickly, so we make use of the &lt;a href="https://reactjs.org/docs/context.html" rel="noopener noreferrer"&gt;Context API&lt;/a&gt; to avoid passing ever-changing props to all the different components that need to know about the many states. &lt;/p&gt;

&lt;p&gt;Six contexts make up what we refer to as our call loop. They handle four different sets of state: devices, tracks, participants, and call state, in addition to a waiting room experience and the overall user interface.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.js&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;UIStateProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CallProvider&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ParticipantsProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TracksProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MediaDeviceProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;WaitingRoomProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/WaitingRoomProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;            &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MediaDeviceProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/TracksProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ParticipantsProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/CallProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/UIStateProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of the contexts also make use of &lt;a href="https://reactjs.org/docs/hooks-custom.html" rel="noopener noreferrer"&gt;custom hooks&lt;/a&gt; that abstract some complexity, depending on the, well, context.&lt;/p&gt;

&lt;p&gt;With that pun out of the way, let’s dive into each of the contexts except for &lt;code&gt;&amp;lt;WaitingRoomProvider&amp;gt;&lt;/code&gt;, You’ll have to...wait for a post on that one. &lt;/p&gt;

&lt;p&gt;Okay, really, we’re ready now. &lt;/p&gt;

&lt;h3&gt;
  
  
  Managing devices
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/shared/contexts/MediaDeviceProvider.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;MediaDeviceProvider&amp;gt;&lt;/code&gt;&lt;/a&gt; grants the entire app access to the cams and mics used during the call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// MediaDeviceProvider.js&lt;/span&gt;

&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;MediaDeviceContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;
     &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
       &lt;span class="nx"&gt;cams&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;mics&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;speakers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;camError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;micError&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;currentDevices&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;deviceState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;setMicDevice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;setCamDevice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;setSpeakersDevice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;}}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/MediaDeviceContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;MediaDeviceProvider&amp;gt;&lt;/code&gt; relies on a &lt;a href="https://github.com/daily-demos/examples/blob/be5d3decfc0ae2e871506430d81561fa0a8ff9c4/dailyjs/shared/hooks/useDevices.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;useDevices&lt;/code&gt;&lt;/a&gt; hook to listen for changes to the call object to make sure the app has an up to date list of the devices on the call and each device’s state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useDevices.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;updateDeviceState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enumerateDevices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;mic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;speaker&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInputDevices&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultCam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;videoDevices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;videoinput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setCams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="nx"&gt;defaultCam&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;videoDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sortByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
       &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultMic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;micDevices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audioinput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setMics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="nx"&gt;defaultMic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;micDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sortByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
       &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;defaultSpeaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;speakerDevices&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;devices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kind&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;audiooutput&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;d&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;deviceId&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="nf"&gt;setSpeakers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="nx"&gt;defaultSpeaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;speakerDevices&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;sortByKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;label&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
       &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;

     &lt;span class="nf"&gt;setCurrentDevices&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="nx"&gt;camera&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;mic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;speaker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;setDeviceState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DEVICE_STATE_NOT_SUPPORTED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useDevices&lt;/code&gt; also handles device errors, like if a cam or mic is blocked, and updates a device’s state when something changes for the participant using the device, like if their tracks change.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Keeping track of tracks
&lt;/h3&gt;

&lt;p&gt;Different devices share different kinds of tracks. A microphone shares an &lt;code&gt;audio&lt;/code&gt; type track; a camera shares &lt;code&gt;video&lt;/code&gt;. Each track contains its own state: playable, loading, off, etc. &lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/shared/contexts/TracksProvider.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;TracksProvider&amp;gt;&lt;/code&gt;&lt;/a&gt; simplifies keeping track of all those tracks as the number of call participants grows. This context listens for changes in track state and dispatches updates. One type of change, for example, could be when a participant’s tracks start or stop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// TracksProvider.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TracksProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tracksReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;initialTracksState&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

 &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStarted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRACK_STARTED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStopped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TRACK_STOPPED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;});&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="cm"&gt;/** Other things happen here **/&lt;/span&gt;

   &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;track-started&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStarted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;track-stopped&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStopped&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling participants
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/shared/contexts/ParticipantsProvider.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;ParticipantsProvider&amp;gt;&lt;/code&gt;&lt;/a&gt; makes sure any and all participant updates are available across the app. It listens for &lt;a href="https://docs.daily.co/reference#participant-events?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;participant events&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ParticipantsProvider.js&lt;/span&gt;

 &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;events&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;joined-meeting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-joined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;];&lt;/span&gt;

   &lt;span class="c1"&gt;// Listen for changes in state&lt;/span&gt;
   &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleNewParticipantsState&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

   &lt;span class="c1"&gt;// Stop listening for changes in state&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
     &lt;span class="nx"&gt;events&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
       &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleNewParticipantsState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleNewParticipantsState&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And dispatches state updates depending on the event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ParticipantsProvider.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleNewParticipantsState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-joined&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
           &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPANT_JOINED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;});&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
           &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPANT_UPDATED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;});&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;participant-left&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
           &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PARTICIPANT_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="p"&gt;});&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
       &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
         &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;ParticipantsProvider&amp;gt;&lt;/code&gt; also calls on &lt;a href="https://www.npmjs.com/package/use-deep-compare" rel="noopener noreferrer"&gt;use-deep-compare&lt;/a&gt; to memoize expensive calculations, like all of the participants on the call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ParticipantsProvider.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;allParticipants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDeepCompareMemo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Managing room and call state
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;&amp;lt;CallProvider&amp;gt;&lt;/code&gt; handles configuration and state for the room where the call happens, where all those devices, participants, and tracks interact. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/shared/contexts/CallProvider.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;CallProvider&amp;gt;&lt;/code&gt;&lt;/a&gt; imports the abstraction hook &lt;a href="https://github.com/daily-demos/examples/blob/main/dailyjs/shared/contexts/useCallMachine.js?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;&lt;code&gt;useCallMachine&lt;/code&gt;&lt;/a&gt; to manage call state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CallProvider.js&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallMachine&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
   &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;useCallMachine&lt;/code&gt; listens for changes in call access, for example, and updates overall call state accordingly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// useCallMachine.js&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;access-state-updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleAccessStateUpdated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;off&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;access-state-updated&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleAccessStateUpdated&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handleAccessStateUpdated&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Other things happen here&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleAccessStateUpdated&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;CALL_STATE_ENDED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CALL_STATE_AWAITING_ARGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CALL_STATE_READY&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
         &lt;span class="nx"&gt;state&lt;/span&gt;
       &lt;span class="p"&gt;)&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ACCESS_STATE_UNKNOWN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
       &lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ACCESS_STATE_NONE&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;setState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;CALL_STATE_NOT_ALLOWED&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;

     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;meetingState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;meetingState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ACCESS_STATE_LOBBY&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
       &lt;span class="nx"&gt;meetingState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;MEETING_STATE_JOINED&lt;/span&gt;
     &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
 &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;lt;CallProvider&amp;gt;&lt;/code&gt; then uses that information, to do things like verify a participant’s access to a room, and whether or not they’re permitted to join the call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// CallProvider.js&lt;/span&gt;

&lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;accessState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;access&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ACCESS_STATE_UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;requiresPermission&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;access&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;level&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;ACCESS_STATE_LOBBY&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nf"&gt;setPreJoinNonAuthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;requiresPermission&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;daily&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the participant requires permission to join, and they’re not joining with a token, then the participant will not be allowed into the call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating Daily meeting tokens with Next.js
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.daily.co/reference#meeting-tokens?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;Meeting tokens&lt;/a&gt; control room access and session configuration on a per-user basis. They’re also a great use case for &lt;a href="https://nextjs.org/docs/api-routes/introduction" rel="noopener noreferrer"&gt;Next API routes&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;API routes let us query endpoints directly within our app, so we don’t have to maintain a separate server. We call the Daily &lt;code&gt;/meeting-tokens&lt;/code&gt; endpoint in &lt;code&gt;/pages/api/token.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/api/token.js&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isOwner&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

 &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAILY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
       &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;room_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;is_owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;isOwner&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;}),&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dailyRes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAILY_REST_DOMAIN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/meeting-tokens`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="nx"&gt;options&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;

   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;dailyRes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;

   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DAILY_DOMAIN&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;index.js&lt;/code&gt;, we fetch the endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// pages/index.js&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/token&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;application/json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;roomName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;room&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isOwner&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;resJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What’s Next.js?
&lt;/h2&gt;

&lt;p&gt;Please &lt;a href="https://github.com/daily-demos/examples?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;fork, clone&lt;/a&gt;, and hack away! There are lots of ways you could start building on top of this demo: adding custom user authentication, &lt;a href="https://www.daily.co/blog/three-ways-to-add-chat-to-your-video-calls-with-the-daily-api/?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;building a chat component&lt;/a&gt;, or pretty much anything that springs to mind. &lt;/p&gt;

&lt;p&gt;We’d appreciate &lt;a href="https://www.daily.co/contact?utm_source=dev&amp;amp;utm_campaign=dev-next-js-and-daily" rel="noopener noreferrer"&gt;hearing what you think&lt;/a&gt; about the demo, especially how we could improve it. We’re also curious about other framework and meta-framework specific sample code that you’d find useful. &lt;/p&gt;

&lt;p&gt;If you’re hoping for more Daily and Next.js sample code, we’ve got you covered. Come back soon! &lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>video</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Optional chaining in the ~real world (React video chat app)</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Thu, 29 Apr 2021 03:33:14 +0000</pubDate>
      <link>https://dev.to/kimberleejohnson/optional-chaining-in-the-real-world-react-video-chat-app-5dm8</link>
      <guid>https://dev.to/kimberleejohnson/optional-chaining-in-the-real-world-react-video-chat-app-5dm8</guid>
      <description>&lt;p&gt;I started learning JavaScript in 2019, around the time that optional chaining became a thing. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The optional chaining operator (&lt;code&gt;?.&lt;/code&gt;) enables you to read the value of a property located deep within a chain of connected objects without having to check that each reference in the chain is valid. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining" rel="noopener noreferrer"&gt;(MDN)&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I remember hearing the hearsay about why this was awesome, but, at the time, the above explanation &lt;a href="https://dev.to/laurieontech/optional-chaining-has-arrived-111l"&gt;and others&lt;/a&gt; and any conversations about a question mark in javascript that isn't the ternary operator still went a bit over my head. Fast forward two years, and I've finally run into optional chaining in the ~real world. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/klGl9A6AkjKL9I8piK/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/klGl9A6AkjKL9I8piK/giphy.gif" alt="Gif of Real World cast reads I thought I was being punk'd"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This post shares that encounter! I'll go over video chat participant "tracks" at the highest level, and then walk through why optional chaining makes sense in this use case. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Before diving in, a quick thank you to my colleague Jess for the lesson that became this post. I'm lucky to learn from her at work everyday and &lt;a href="https://twitter.com/gemontracks" rel="noopener noreferrer"&gt;on Twitter&lt;/a&gt; too!&lt;/em&gt; &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Video chat participant tracks
&lt;/h2&gt;

&lt;p&gt;Like a lot of people, I've been on a lot of video calls this year. I also work at Daily, where my colleagues build &lt;a href="https://www.daily.co/?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;real-time audio and video APIs&lt;/a&gt;. I write &lt;a href="https://docs.daily.co/reference#using-the-dailyco-front-end-library?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; for the tools they build and prototype &lt;a href="https://github.com/daily-demos?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;demo apps&lt;/a&gt;, so I'm learning a fair amount about the different moving parts behind video and &lt;a href="https://www.daily.co/blog/how-to-build-a-billion-dollar-audio-app-in-a-weekend/?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;audio-only&lt;/a&gt; calls, things I didn't really think about before. &lt;/p&gt;

&lt;p&gt;Take, for example, tracks! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/kyj9z12kYr9fRr9T8X/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/kyj9z12kYr9fRr9T8X/giphy.gif" alt="Runners on a track"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I join a video call with someone else, I and that other person or people trade audio, video, and sometimes screen &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MediaStreamTrack" rel="noopener noreferrer"&gt;media tracks&lt;/a&gt; back and forth. &lt;/p&gt;

&lt;p&gt;As you've probably experienced, participants' tracks can go through &lt;em&gt;many&lt;/em&gt; states. Tracks load as participants join, and then they're playable; they can be muted intentionally or because of a disruption. The &lt;a href="https://www.daily.co/?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;Daily API&lt;/a&gt; accounts for the following participant track states, for example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;blocked&lt;/li&gt;
&lt;li&gt;off&lt;/li&gt;
&lt;li&gt;sendable&lt;/li&gt;
&lt;li&gt;loading&lt;/li&gt;
&lt;li&gt;playable&lt;/li&gt;
&lt;li&gt;interrupted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can find a track's state on the &lt;a href="https://docs.daily.co/reference#%EF%B8%8F-participants?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;Daily participants object&lt;/a&gt;. The object's keys are session id's for each participant, and the corresponding values include lots of details about the participant. For example, here's the participant object for a session_id &lt;code&gt;"e20b7ead-54c3-459e-800a-ca4f21882f2f"&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"e20b7ead-54c3-459e-800a-ca4f21882f2f"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;user_id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e20b7ead-54c3-459e-800a-ca4f21882f2f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;audio:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;video:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;screen:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;joined_at:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Date(&lt;/span&gt;&lt;span class="mi"&gt;2019-04-30&lt;/span&gt;&lt;span class="err"&gt;T&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;06&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;32.485&lt;/span&gt;&lt;span class="err"&gt;Z)&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;local:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;owner:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;session_id:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"e20b7ead-54c3-459e-800a-ca4f21882f2f"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;user_name:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;tracks:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;audio:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;subscribed:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;state:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;'playable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;blocked?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;byDeviceMissing?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;byPermissions?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;off?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;byUser?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="err"&gt;byBandwidth?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;boolean&lt;/span&gt;&lt;span class="w"&gt; 
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="err"&gt;track?:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;MediaStreamTrack&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;video:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;same&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;above&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;screenAudio:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;same&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;above&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="err"&gt;screenVideo:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;same&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;above&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;*/&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The track's state is deeply nested at &lt;code&gt;participant.tracks.track.state&lt;/code&gt;, where track stands for the kind of track (audio, video, screenAudio or screenVideo). &lt;/p&gt;

&lt;p&gt;And this is where optional chaining comes in. &lt;/p&gt;

&lt;h2&gt;
  
  
  Opting into optional chaining
&lt;/h2&gt;

&lt;p&gt;In JavaScript, if an object doesn't exist, trying to access values on that object throws an error. &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%2Fuploads%2Farticles%2Fmvgtvgeuiv2h839v1x8o.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%2Fuploads%2Farticles%2Fmvgtvgeuiv2h839v1x8o.png" alt="Mean Girls Lindsay Lohan meme text reads the object does not exist"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This can be inconvenient when a value we need is deeply nested, like the participant's video/audio track state. Let's look at an example. &lt;/p&gt;

&lt;p&gt;When a participant leaves a call, their audio/video tracks stop. When their audio/video tracks stop, we want to remove their participant tile from the call.&lt;/p&gt;

&lt;p&gt;We handle this update the same way we handle all participant updates. I wrote a longer post about how &lt;a href="https://dev.to/kimberleejohnson/learn-react-hooks-by-looking-under-the-hood-of-a-video-chat-app-3a5o"&gt;React hooks help us manage state in this video chat app&lt;/a&gt;, but tl; dr: the useEffect hook listens for changes to &lt;code&gt;participantUpdated&lt;/code&gt; state, and on that change updates the rendered &lt;code&gt;participants&lt;/code&gt; list.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;participantUpdated&lt;/code&gt; stores a string including the name of the event, that participant's session id, and the time the event happened. When a participant's tracks stop, as for other events, we call &lt;code&gt;setParticipantUpdated&lt;/code&gt; to change the string. Here's how that looks without optional chaining:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStopped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;logDailyEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setParticipantUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s2"&gt;`track-stopped-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you guess why this might cause a problem? &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/1xmBLaMM6ciAYvuNCG/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/1xmBLaMM6ciAYvuNCG/giphy.gif" alt="Schitts Creek Moira Rose gif reads and that's a problem because"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Because when a participant leaves a call and their tracks stop, they're no longer a meeting participant. They can't be found on the Daily participants object. &lt;code&gt;.participant&lt;/code&gt; does not exist. The console throws an error, &lt;code&gt;Cannot read property 'user_id' of null&lt;/code&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%2Fuploads%2Farticles%2Fs6qj2jlmjocg2f6fdhwv.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%2Fuploads%2Farticles%2Fs6qj2jlmjocg2f6fdhwv.png" alt="Console error reads TypeError and cannot read property user id of null"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From a UI perspective, a black, trackless tile remains even after the participant leaves. This is because &lt;code&gt;setParticipantUpdated&lt;/code&gt; can't fire, so the hook listening for the change doesn't update the rendered participant list to remove the absent participant, even though their tracks disappear. &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%2Fuploads%2Farticles%2F8jzrfqqgg3gwy3tnvix7.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%2Fuploads%2Farticles%2F8jzrfqqgg3gwy3tnvix7.png" alt="Video chat app with audio muted and empty black tile"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optional chaining helps us avoid this. Let's add the syntax to &lt;code&gt;handleTrackStopped&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleTrackStopped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;logDailyEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nf"&gt;setParticipantUpdated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="s2"&gt;`track-stopped-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, those &lt;code&gt;.?&lt;/code&gt; evaluate the missing &lt;code&gt;.participant&lt;/code&gt; as undefined. If I add a &lt;code&gt;console.log()&lt;/code&gt; to &lt;code&gt;handleTrackStopped&lt;/code&gt; to see the string passed to state, that's confirmed: &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%2Fuploads%2Farticles%2F7irgvwxus8gbt7wirr7x.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%2Fuploads%2Farticles%2F7irgvwxus8gbt7wirr7x.png" alt="Console reads track-stopped-undefined"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With this successful change to &lt;code&gt;participantUpdated&lt;/code&gt; state, our hook can register the change, update the participant list, and be sure to remove any trackless tiles. &lt;/p&gt;

&lt;h2&gt;
  
  
  Remember, it's all optional
&lt;/h2&gt;

&lt;p&gt;Optional chaining makes sense in this &lt;a href="https://github.com/daily-demos/call-object-react?utm_source=dev&amp;amp;utm_campaign=optional-chaining" rel="noopener noreferrer"&gt;demo video chat app&lt;/a&gt; for a few reasons. For one thing, our track state data was pretty deeply nested. For another, it's okay if the &lt;code&gt;.participant&lt;/code&gt; doesn't exist in our app after they leave (we won't be trying to access their data again once they're gone). &lt;/p&gt;

&lt;p&gt;We didn't use optional chaining as our default syntax for every nested object in our app, and it's unlikely that would ever be a good idea. If you're using this syntax in the ~real world, be sure to be explicit about it. &lt;/p&gt;

&lt;p&gt;And, if you are using optional chaining, please tell me about it! When have you opted for it recently? Let me know in the comments or &lt;a href="https://twitter.com/kimeejohnson" rel="noopener noreferrer"&gt;over on Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to stream a local file into a real-time video call</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Fri, 16 Apr 2021 21:41:39 +0000</pubDate>
      <link>https://dev.to/trydaily/a-workaround-for-watching-videos-together-on-calls-2hbh</link>
      <guid>https://dev.to/trydaily/a-workaround-for-watching-videos-together-on-calls-2hbh</guid>
      <description>&lt;p&gt;We've joined a lot of video calls over the last year here at Daily. While it’s easy to chat in real-time with someone on the other side of the world, streaming a video to watch simultaneously as part of that conversation quickly complicates things. We’ve all been in meetings, training sessions, discussion groups, and even catch ups with friends where lags and buggy streams make it impossible to watch a video at the same time. &lt;/p&gt;

&lt;p&gt;With great frustration comes great experimentation, and we’ve come up with a quick hack for streaming local video files during our calls: using the Daily API &lt;a href="https://docs.daily.co/reference#%EF%B8%8F-startscreenshare?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;startScreenShare()&lt;/a&gt; method. &lt;/p&gt;

&lt;p&gt;This tutorial covers our workaround! We’ll go over uploading the local file and creating a stream from it, sharing that stream, and making sure that our hack is working. &lt;/p&gt;

&lt;p&gt;If you’re looking to implement this feature in your own &lt;a href="https://docs.daily.co/docs/embed-the-daily-prebuilt-ui?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;embedded prebuilt UI&lt;/a&gt; or &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface#daily-call-object?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;Daily call object&lt;/a&gt; video app, you can add to your own code as we go. Or, if you want to fiddle with a working prototype, download &lt;a href="https://gist.github.com/kimberleejohnson/97ef1f01ea8cb63625cdd588b07003b5?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;our gist&lt;/a&gt; and &lt;a href="https://docs.daily.co/docs/create-and-manage-rooms-with-the-rest-api?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;create a Daily room&lt;/a&gt; if you don’t have one already. &lt;/p&gt;

&lt;h2&gt;
  
  
  Before we get started
&lt;/h2&gt;

&lt;p&gt;We put it in the title and we mean it: this is a hack. It’s a workaround, not meant for production, but for low stakes, internal use cases. Somebody may or may not have debuted it by sneaking &lt;a href="https://www.youtube.com/watch?v=_Yhyp-_hX2s" rel="noopener noreferrer"&gt;mom’s spaghetti&lt;/a&gt; into a Daily all hands. &lt;/p&gt;

&lt;p&gt;As with any hack, it also comes with a few caveats:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If you’ll be using Chrome, you’ll need to &lt;a href="https://www.lifewire.com/hardware-acceleration-in-chrome-4125122" rel="noopener noreferrer"&gt;disable hardware acceleration&lt;/a&gt; before you share a video stream. &lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Unfortunately, this solution doesn’t work for streaming video from Safari, because Safari doesn’t support &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream" rel="noopener noreferrer"&gt;captureStream()&lt;/a&gt;.  &lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With that, here we go here’s our shot, feet fail us not. &lt;/p&gt;

&lt;h2&gt;
  
  
  Upload the local video file and create a stream
&lt;/h2&gt;

&lt;p&gt;We need an &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element with three attributes to upload the video: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: so that we can use &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementById" rel="noopener noreferrer"&gt;&lt;code&gt;document.getElementById&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/tags/att_input_type.asp" rel="noopener noreferrer"&gt;&lt;code&gt;type&lt;/code&gt;&lt;/a&gt;: to specify that this input field is for a file upload. &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.w3schools.com/tags/att_input_accept.asp" rel="noopener noreferrer"&gt;&lt;code&gt;accept&lt;/code&gt;&lt;/a&gt;: to make sure the uploaded file is a video.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"vid-file-picker"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"file"&lt;/span&gt; &lt;span class="na"&gt;accept=&lt;/span&gt;&lt;span class="s"&gt;"video/*"&lt;/span&gt;&lt;span class="err"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We add an event listener to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. On &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event" rel="noopener noreferrer"&gt;&lt;code&gt;'change'&lt;/code&gt;&lt;/a&gt;, we call our function &lt;code&gt;playLocalVideoFile()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;videoInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;vid-file-picker&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;videoInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playLocalVideoFile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;playLocalVideoFile()&lt;/code&gt; creates a video stream from the file using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/captureStream" rel="noopener noreferrer"&gt;captureStream()&lt;/a&gt; or mozCaptureStream() if you’re using FireFox.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;playLocalVideoFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;evt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;videoEl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;local-vid&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;canPlayType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nf"&gt;alert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cannot play that file&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;URL&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createObjectURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;play&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Mozilla currently prefixes the function name, so we have to check for either&lt;/span&gt;
   &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mozCaptureStream&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mozCaptureStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;captureStream&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localVideoStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;videoEl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;captureStream&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that we have the stream, we can share it. &lt;/p&gt;

&lt;h2&gt;
  
  
  Share the stream with call participants
&lt;/h2&gt;

&lt;p&gt;We add a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; to &lt;code&gt;shareVideo()&lt;/code&gt; through the screen share stream.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;onclick=&lt;/span&gt;&lt;span class="s"&gt;"shareVideo()"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;share video through screenshare stream&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;shareVideo()&lt;/code&gt; calls the Daily &lt;a href="https://docs.daily.co/reference#%EF%B8%8F-startscreenshare?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;startScreenShare()&lt;/a&gt; method on the &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface#daily-call-object?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;Daily call object&lt;/a&gt;, passing the &lt;code&gt;localVideoStream&lt;/code&gt; created via &lt;code&gt;playLocalVideo()&lt;/code&gt; as the media stream to be shared:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;shareVideo&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startScreenShare&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;mediaStream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;localVideoStream&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test the stream
&lt;/h2&gt;

&lt;p&gt;Make sure you have a &lt;a href="https://docs.daily.co/docs/create-and-manage-rooms-with-the-rest-api?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;Daily room URL&lt;/a&gt; ready. We hardcoded ours as a &lt;code&gt;const&lt;/code&gt; in our html file because, again, this is a hack. Relatedly, if you’re using Chrome, don’t forget to &lt;a href="https://www.lifewire.com/hardware-acceleration-in-chrome-4125122" rel="noopener noreferrer"&gt;disable hardware acceleration&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Next, open the file in your browser. In a new tab, visit your Daily room URL. You should see two participants in the room tab, but just you in the file tab. &lt;/p&gt;

&lt;p&gt;Click "Choose File" and upload the local video of your choice. If you need inspiration for what file to choose, try searching on &lt;a href="https://www.pexels.com/videos/" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt; for puppies (that’s what I did). Finally, click “share video through screen share stream” and in the tab with the room URL you should see the video coming through. &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%2Fuploads%2Farticles%2Fniayloktucxucyxv477i.gif" 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%2Fuploads%2Farticles%2Fniayloktucxucyxv477i.gif" alt="gif shows video call where video of a puppy plays on the biggest video stream "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Snap back to reality (what’s next)
&lt;/h2&gt;

&lt;p&gt;That’s it! Thanks for reading. We hope this makes sharing videos for everyone to watch on your calls easier. Let us know in the comments!&lt;/p&gt;

&lt;p&gt;And, if you’re interested in building more apps that are all about sharing and streaming content, our &lt;a href="https://www.daily.co/blog/webinartc-building-a-webinar-app-with-react-and-daily-prebuilt-ui/?utm_source=dev&amp;amp;utm_campaign=stream-local-video" rel="noopener noreferrer"&gt;webinar series&lt;/a&gt; might spark some ideas. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>tutorial</category>
      <category>webrtc</category>
    </item>
    <item>
      <title>Learn React Hooks by looking under the hood of a video chat app </title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Thu, 01 Apr 2021 05:48:22 +0000</pubDate>
      <link>https://dev.to/kimberleejohnson/learn-react-hooks-by-looking-under-the-hood-of-a-video-chat-app-3a5o</link>
      <guid>https://dev.to/kimberleejohnson/learn-react-hooks-by-looking-under-the-hood-of-a-video-chat-app-3a5o</guid>
      <description>&lt;p&gt;Like most developers I know, I learn a lot every day at work. That's part of why I wanted to make programming part of my job! &lt;/p&gt;

&lt;p&gt;I really like getting to immediately &lt;em&gt;apply&lt;/em&gt; new things, and that helps me learn better, too. Despite reading lots of explainers and a few tutorials, I didn't really start to grok React Hooks until I needed to dig into the &lt;a href="https://github.com/daily-demos/call-object-react?utm_source=dev&amp;amp;utm_campaign=learn-react-hooks"&gt;Daily React video chat demo&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;When I think about the roles &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useMemo&lt;/code&gt;, and &lt;code&gt;useCallback&lt;/code&gt; play in a video chat app, something a lot of us use every day, I better remember how each hook works and recognize other opportunities to use them. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/B4ZgcoPTHYXL2/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/B4ZgcoPTHYXL2/giphy.gif" alt="Dustin Hoffman smiles as Captain Hook"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;In case reading about that practical application could help you too, I decided to write about it! After a quick Hooks refresher, we'll look at an example of each these Hooks in the Daily &lt;a href="https://call-object-react.netlify.app/?utm_source=dev&amp;amp;utm_campaign=learn-react-hooks"&gt;demo app&lt;/a&gt;, and why we decided to use each. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;I mentioned in my last post about &lt;a href="https://dev.to/kimberleejohnson/how-i-learned-to-avoid-implied-globals-and-why-16ad"&gt;avoiding global variables&lt;/a&gt; that I'm trying to get better about sharing what my colleagues teach me. This round is brought to us by the inimitable &lt;a href="https://twitter.com/gemontracks"&gt;Jess Mitchell&lt;/a&gt;, who I am so grateful to work with and learn from!&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Before we get hook'd
&lt;/h2&gt;

&lt;p&gt;I really liked &lt;a href="%5Bhttps://dev.to/aspittel%5D(https://dev.to/aspittel)"&gt;Ali Spittel's&lt;/a&gt; definition of Hooks on the &lt;a href="%5Bhttps://www.ladybug.dev/episodes/getting-hooked-on-react-part-2%5D(https://www.ladybug.dev/episodes/getting-hooked-on-react-part-2)"&gt;latest Ladybug podcast&lt;/a&gt;: &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Hooks are a special type of function that allow you to hook into the component lifecycle of react components. So they allow you to do special things that a normal function wouldn't be able to do."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This means we can do unique things when components mount, update, and unmount. Like the &lt;a href="https://reactjs.org/docs/hooks-overview.html"&gt;docs say&lt;/a&gt;, we can take advantage of state and other features without having to write class components. &lt;/p&gt;

&lt;p&gt;With that overview in mind, let's look at three Hooks in our video chat app: &lt;code&gt;useEffect&lt;/code&gt;, &lt;code&gt;useMemo,&lt;/code&gt; and &lt;code&gt;useCallback&lt;/code&gt;. &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;code&gt;useEffect&lt;/code&gt; to manage participant updates in state
&lt;/h2&gt;

&lt;p&gt;With &lt;a href="https://reactjs.org/docs/hooks-effect.html"&gt;&lt;code&gt;useEffect&lt;/code&gt;&lt;/a&gt;, we can, well, perform side effects in function components, based on state or prop changes. &lt;/p&gt;

&lt;p&gt;In a video chat app, lots of things happen! Participants join and leave calls, start and stop their audio and video tracks, and then some. Our UI needs to update along with these changes. For example, it needs to add and remove video tracks as participants come and go. &lt;/p&gt;

&lt;p&gt;The &lt;a href="https://docs.daily.co/docs?utm_source=dev&amp;amp;utm_campaign=learn-react-hooks"&gt;Daily API&lt;/a&gt; fires corresponding &lt;a href="https://docs.daily.co/reference#events?utm_source=dev&amp;amp;utm_campaign=learn-react-hooks"&gt;events&lt;/a&gt; as these things happen, e.g. &lt;code&gt;'participant-joined'&lt;/code&gt;, &lt;code&gt;'track-stopped'&lt;/code&gt;, etc. In our video chat app, we listen for these events and their handlers set our &lt;code&gt;particpantUpdated&lt;/code&gt; state in response. &lt;/p&gt;

&lt;p&gt;Here's where &lt;code&gt;useEffect&lt;/code&gt; comes in! We only need to update the UI when a change has happened, when &lt;code&gt;participantUpdated&lt;/code&gt; is set. We pass &lt;code&gt;participantUpdated&lt;/code&gt; as a dependency (along with the call object that contains the updated participant data) to a &lt;code&gt;useEffect&lt;/code&gt; hook, so we only update our participant list when something has changed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participantUpdated&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;list&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
      &lt;span class="nx"&gt;setParticipants&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;list&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;participantUpdated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That covers storing the participant list, but what about displaying participants, rendering their video and audio tracks? That's where our next hook comes in. &lt;/p&gt;


&lt;h2&gt;
  
  
  &lt;code&gt;useMemo&lt;/code&gt; to re-render videos only when we have to
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://reactjs.org/docs/hooks-reference.html#usememo"&gt;&lt;code&gt;useMemo&lt;/code&gt;&lt;/a&gt; returns a memoized value. &lt;a href="https://en.wikipedia.org/wiki/Memoization"&gt;Memoized&lt;/a&gt; means a value that's the result of an expensive function call. &lt;/p&gt;

&lt;p&gt;There are lots of expensive calculations in a video chat app. Each participant's audio and video track alone contains loads of data, and computing that on every render would be a lot. &lt;/p&gt;

&lt;p&gt;Instead, we pass our &lt;code&gt;participants&lt;/code&gt; state value as a dependency to the &lt;code&gt;useMemo&lt;/code&gt; hook that displays our tiles.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayLargeTiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useMemo&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLarge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"large-tiles"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;tiles&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Tile&lt;/span&gt;
              &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`large-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;videoTrackState&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;audioTrackState&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;audio&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;isLarge&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLarge&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;disableCornerMessage&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isScreenShare&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
              &lt;span class="na"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;
                  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
                  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                      &lt;span class="nx"&gt;sendHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="p"&gt;}&lt;/span&gt;
              &lt;span class="si"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useMemo&lt;/code&gt; hook lets us only change the videos displayed when the &lt;code&gt;participants&lt;/code&gt; have changed, instead of recalculating on every render. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Note: Because we built a video chat app with participant changes that happen often, the performance benefits from&lt;code&gt;useMemo&lt;/code&gt; are significant; it saves us many recalculations. In a lot of cases, though, this hook might be more than your application needs. &lt;code&gt;useMemo&lt;/code&gt; has a reputation of &lt;a href="https://dev.to/bnevilleoneill/you-re-overusing-usememo-rethinking-hooks-memoization-19g8"&gt;being overused&lt;/a&gt;, so tread with caution.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;


&lt;h2&gt;
  
  
  &lt;code&gt;useCallback&lt;/code&gt; to re-render &lt;code&gt;startLeavingCall()&lt;/code&gt; function only when we have to
&lt;/h2&gt;

&lt;p&gt;Just like &lt;code&gt;useMemo&lt;/code&gt; prevents us from re-calculating values that haven't changed, &lt;a href="https://reactjs.org/docs/hooks-reference.html#usecallback"&gt;&lt;code&gt;useCallback&lt;/code&gt;&lt;/a&gt; lets us stop specific functions from re-rendering. &lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://github.com/daily-demos/call-object-react/blob/02e54fef364040175b0ec6222ba28552b69aa948/src/components/App/App.js?utm_source=dev&amp;amp;utm_campaign=learn-react-hooks"&gt;App.js component&lt;/a&gt;, lots of things can trigger a re-render. But our &lt;code&gt;startLeavingCall&lt;/code&gt; function, for example, only needs to re-render if the &lt;code&gt;callObject&lt;/code&gt;, which stores data about our call, or our &lt;code&gt;appState&lt;/code&gt; changes. This is because the function does different things depending on those values. &lt;/p&gt;

&lt;p&gt;We pass &lt;code&gt;callObject&lt;/code&gt; and &lt;code&gt;appState&lt;/code&gt; as our dependencies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
   * Starts leaving the current call.
   */&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;startLeavingCall&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useCallback&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// If we're in the error state, we've already "left", so just clean up&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;appState&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;STATE_ERROR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;destroy&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;setRoomUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;setCallObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STATE_IDLE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;setAppState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;STATE_LEAVING&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;leave&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;appState&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;h2&gt;
  
  
  Hook'd and wanting more?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/oiYnN3ba0YPkY/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/oiYnN3ba0YPkY/giphy.gif" alt="Rufio from the movie Hook"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this helped make Hooks feel a little more applicable! Can you think of any new ways to apply &lt;code&gt;useEffect&lt;/code&gt; or &lt;code&gt;useMemo&lt;/code&gt;, or &lt;code&gt;useCallback&lt;/code&gt; in apps that you're building? Tell me in the comments!  Especially tell me if you'll be building any video (or &lt;a href="https://www.daily.co/blog/how-to-build-a-billion-dollar-audio-app-in-a-weekend/"&gt;audio!&lt;/a&gt;) apps. You can give me a shout over on &lt;a href="https://twitter.com/kimeejohnson"&gt;Twitter too&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>How I learned to avoid implied globals (and why)</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Tue, 02 Mar 2021 18:19:54 +0000</pubDate>
      <link>https://dev.to/kimberleejohnson/how-i-learned-to-avoid-implied-globals-and-why-16ad</link>
      <guid>https://dev.to/kimberleejohnson/how-i-learned-to-avoid-implied-globals-and-why-16ad</guid>
      <description>&lt;p&gt;Before I started drafting pull requests, I used to draft press releases. My public relations background comes in handy in my DevRel role today, and it helps me keep learning, too. I’m a community-taught developer, picking up a lot of my technical skills from people I meet in communities like &lt;a href="https://girlgeek.io/" rel="noopener noreferrer"&gt;Girl Geek Dinner&lt;/a&gt;, the &lt;a href="https://www.ccsf.edu/academics/schools/stem/computer-science-department" rel="noopener noreferrer"&gt;CS department at CCSF&lt;/a&gt;, and, of course, &lt;a href="https://dev.to/"&gt;DEV&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Today, I’m lucky and grateful to also learn on the job, from colleagues patient enough to teach me best practices. In the spirit of Laurie’s tweet, I’m going to try to do a better job of sharing what they teach me. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1337179838818492417-999" src="https://platform.twitter.com/embed/Tweet.html?id=1337179838818492417"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1337179838818492417-999');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1337179838818492417&amp;amp;theme=dark"
  }



 &lt;/p&gt;

&lt;p&gt;This post is my first pass at that! Read on to learn how I learned to be less afraid of spooky JavaScript Promises, to avoid implied global variables, and to get better at bridging the gap between what I know and what my colleagues can teach me. &lt;/p&gt;
&lt;h2&gt;
  
  
  Spooky Promises from scary code
&lt;/h2&gt;

&lt;p&gt;When I built a &lt;a href="https://dev.to/trydaily/spook-your-colleagues-with-a-halloween-themed-virtual-standup-52m1"&gt;Halloween themed video call demo to prank the team&lt;/a&gt;, in addition to setting up the &lt;a href="https://docs.daily.co/docs/embed-the-daily-prebuilt-ui" rel="noopener noreferrer"&gt;video call elements&lt;/a&gt; my main &lt;code&gt;run()&lt;/code&gt; function needed to retrieve a list of gifs from the Giphy API, and then to plop a random gif on the page.  &lt;/p&gt;

&lt;p&gt;Here’s the original code I wrote to do that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;     
   &lt;span class="nf"&gt;getGifs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
              &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;giphs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bday&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Some other things happen here too &lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While this code worked, you might’ve noticed the same thing &lt;a href="https://dev.to/philmillman"&gt;Phil&lt;/a&gt; did: &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%2Fuploads%2Farticles%2Fvmyzxu209bwia3hu9rgw.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%2Fuploads%2Farticles%2Fvmyzxu209bwia3hu9rgw.png" alt="GitHub comment suggests using the return value instead of implied global"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re at a similar spot in your programming journey to where I was before I wrote this post, your first thought reading his comment might’ve been, "Oh! I just need to store the return value of getGifs inside a variable." &lt;/p&gt;

&lt;p&gt;This first attempt led to bad news bears, or, a lot of pending Promises in my &lt;code&gt;spooky.html&lt;/code&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%2Fuploads%2Farticles%2Fi5mlz4ok9fq6fkeqfg58.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%2Fuploads%2Farticles%2Fi5mlz4ok9fq6fkeqfg58.png" alt="Console errors read repeatedly "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oh no. Promises. They’re on almost every &lt;a href="https://dev.to/macmacky/70-javascript-interview-questions-5gfi"&gt;interview questions list&lt;/a&gt;, but I somehow got this job even though I’m still a bit scared seeing these errors, what am I even doing?!? &lt;/p&gt;

&lt;p&gt;Better stop that narrative and take a breath. And then take a Google. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/ToMjGpzjpWhlKkIyiY0/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/ToMjGpzjpWhlKkIyiY0/giphy.gif" alt="Gif magnifying glass searches bar"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Promises and async/await
&lt;/h3&gt;

&lt;p&gt;There are loads of &lt;a href="https://dev.to/lydiahallie/javascript-visualized-promises-async-await-5gke"&gt;fantastic articles about JavaScript Promises and async/await&lt;/a&gt; out there. The part I needed to understand to fix my code, the part that Phil helped surface from the noise, is that the async/await pattern is syntactic sugar on top of Promises. &lt;/p&gt;

&lt;p&gt;While I got the &lt;code&gt;async&lt;/code&gt; part of the pattern ahead of my &lt;code&gt;async function run()&lt;/code&gt;, I forgot the &lt;code&gt;await.&lt;/code&gt; Await, well, tells a function to wait on the next step until a Promise resolves. I saw all those &lt;code&gt;{&amp;lt;pending&amp;gt;}&lt;/code&gt; Promises because await was missing. &lt;/p&gt;

&lt;p&gt;With that fixed, I could focus on specifying return values and replacing implied global variables. &lt;/p&gt;

&lt;h3&gt;
  
  
  Variable scope and unpredictable consequences
&lt;/h3&gt;

&lt;p&gt;It’s helpful for me to trace back every step a function makes, so I went back to my &lt;code&gt;getGifs()&lt;/code&gt; function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getGifs&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;INSERT_GIPHY_API_KEY_HERE&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;giphyEndpoint&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://api.giphy.com/v1/gifs/search?api_key=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;q=halloween&amp;amp;rating=pg`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
          &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;giphyEndpoint&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
          &lt;span class="nx"&gt;gifs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;gifs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It’s not just my &lt;code&gt;run()&lt;/code&gt; function, that was missing variable declarations. &lt;code&gt;gifs = await response.json()&lt;/code&gt; in &lt;code&gt;getGifs()&lt;/code&gt; is missing one too. &lt;/p&gt;

&lt;p&gt;When I called &lt;code&gt;getGifs()&lt;/code&gt; in &lt;code&gt;run()&lt;/code&gt;, I was telling the function to create a side effect and change the state of a global variable on the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window" rel="noopener noreferrer"&gt;window object&lt;/a&gt;. If someone else wrote &lt;code&gt;gifs =&lt;/code&gt; somewhere else, that could override the values I actually wanted. &lt;/p&gt;

&lt;p&gt;See what I mean in this codepen. &lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/kimberlee/embed/BaLLogx?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;"Color circles" fills the initial circle colors. Since we didn’t scope the colors as variables within the &lt;code&gt;colorCircles()&lt;/code&gt; function, they became global variables on the window object. That means we can "accidentally" &lt;code&gt;override()&lt;/code&gt; them in the next function, and &lt;code&gt;reset()&lt;/code&gt; them too. &lt;/p&gt;

&lt;p&gt;While that side effect works for the purposes of an example codepen, keeping track of the colors as they get swapped is still hard enough to follow. It's like Elle Woods said: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://i.giphy.com/media/l4FGl9rk3Dp2vfAuQ/giphy.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://i.giphy.com/media/l4FGl9rk3Dp2vfAuQ/giphy.gif" alt="Elle Woods quote Whoever said orange was the new pink was disturbed"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Consequences of implied globals can be greater in larger applications, or even when it comes to picking gifs to prank your colleagues. &lt;/p&gt;

&lt;h2&gt;
  
  
  Final code and final takeaways
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;gifSearchResults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getGifs&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
          &lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
                &lt;span class="nx"&gt;gifSearchResults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;floor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nx"&gt;images&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;original&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
              &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gifs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
              &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
          &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the final code, I’m using the actual response object from my call to &lt;code&gt;getGifs()&lt;/code&gt;. Now, if I want, I can reuse the function in other places, pass in specific search parameters, and use multiple instances of the return object instead of just one globally. Best of all, the state outside of the object won’t get accidentally mutated.  &lt;/p&gt;

&lt;p&gt;After this code review, I know a bit more about how async/await works and principles of good functional programming. Beyond that, I also learned: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Digging around before asking other devs for help can lead to better debugging and faster learning (&lt;a href="https://jvns.ca/blog/good-questions/" rel="noopener noreferrer"&gt;Julia Evans’ post&lt;/a&gt; describes this well!).
&lt;/li&gt;
&lt;li&gt;That said, sometimes sharing learnings in progress can be good too! When I shared my first pass at what I thought I learned with Phil, he helped point out the most important parts. &lt;/li&gt;
&lt;li&gt;Even "silly" projects can teach you useful things. Because I built an app that picked random Halloween gifs, I now better understand why mutating state outside of a function itself is Bad Functional Programming. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Follow your heart! Build what’s fun! Like my friend Chloe says, it’s all &lt;a href="https://twitter.com/kimeejohnson/status/1286426590113480705" rel="noopener noreferrer"&gt;digital crafting&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Let me know what things you’re excited about building over &lt;a href="https://twitter.com/kimeejohnson" rel="noopener noreferrer"&gt;@kimeejohnson&lt;/a&gt;, and especially let me know if you’ll be &lt;a href="https://www.daily.co/?utm_source=dev&amp;amp;utm_medium=post&amp;amp;utm_campaign=learning0" rel="noopener noreferrer"&gt;building something with video chat&lt;/a&gt;. &lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Add text chat to a video call built on React and daily-js</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Wed, 09 Dec 2020 18:34:03 +0000</pubDate>
      <link>https://dev.to/trydaily/add-text-chat-to-a-video-call-built-on-react-and-daily-js-18p0</link>
      <guid>https://dev.to/trydaily/add-text-chat-to-a-video-call-built-on-react-and-daily-js-18p0</guid>
      <description>&lt;p&gt;When Paul &lt;a href="https://dev.to/trydaily/build-a-video-chat-app-in-minutes-with-react-and-daily-js-481c"&gt;built a custom video chat app&lt;/a&gt; using React and the daily-js library, he used &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface"&gt;the Daily call object&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;The 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. With that great access comes great flexibility! But, it also means that features like text chat need to be added from scratch. &lt;/p&gt;

&lt;p&gt;This post walks you through how to do that! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--uvKbD5dC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/vpyw558s4gc2v0tptkaz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uvKbD5dC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/vpyw558s4gc2v0tptkaz.gif" alt="text chat pops up with knock knock joke gif" width="408" height="720"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;We're adding to Paul's demo React video app. When a user clicks to start a call, the app passes a Daily room URL to a new Daily call object, then joins the call. The call object tracks 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. &lt;/p&gt;

&lt;p&gt;We'll use the &lt;a href="https://docs.daily.co/reference#introduction"&gt;&lt;code&gt;sendAppMessage&lt;/code&gt;&lt;/a&gt; method and corresponding &lt;a href="https://docs.daily.co/reference#app-message"&gt;"app-message"&lt;/a&gt; event to add text chat to the app. &lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Daily account: &lt;a href="https://dashboard.daily.co/signup"&gt;Sign up for an account&lt;/a&gt; if you don’t have one already.&lt;/li&gt;
&lt;li&gt;Cloned &lt;code&gt;daily-demos/call-object-react&lt;/code&gt; Github repository: The fastest way to follow along with this tutorial and get a demo app up and running is to clone &lt;a href="https://github.com/daily-demos/call-object-react"&gt;this repo&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Familiarity with React: 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;With that, we're ready to start! &lt;/p&gt;

&lt;h2&gt;
  
  
  Add text chat functionality
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;&amp;lt;Chat&amp;gt;&lt;/code&gt; is a form that does a few things with the participant &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;, meaning their chat message. First, via React’s useState hook and our &lt;code&gt;handleChange&lt;/code&gt; function, it tracks their input in the component state &lt;code&gt;inputValue&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleChange&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When a participant sends a message, they call &lt;code&gt;handleSubmit&lt;/code&gt;, which broadcasts the message, stored in &lt;code&gt;inputValue&lt;/code&gt; to other participants on the call using Daily’s &lt;a href="https://docs.daily.co/reference#introduction"&gt;&lt;code&gt;sendAppMessage&lt;/code&gt;&lt;/a&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sendAppMessage&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;handleSubmit&lt;/code&gt; also updates the sender’s &lt;code&gt;chatHistory&lt;/code&gt; state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt;
     &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt;
     &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="nx"&gt;setChatHistory&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
     &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;inputValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;},&lt;/span&gt;
   &lt;span class="p"&gt;]);&lt;/span&gt;
   &lt;span class="nx"&gt;setInputValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the return statement, &lt;code&gt;&amp;lt;Chat&amp;gt;&lt;/code&gt; maps over every value in &lt;code&gt;chatHistory&lt;/code&gt; to display the messages.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;`entry-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;messageList&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;b&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/b&amp;gt;: {entry.message&lt;/span&gt;&lt;span class="err"&gt;}
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;))}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That covers sending messages. Now, we're ready to tackle making sure the app notifies recipients and displays their messages. &lt;/p&gt;

&lt;h2&gt;
  
  
  Alert participants to new messages
&lt;/h2&gt;

&lt;p&gt;After the Daily &lt;code&gt;sendAppMessage&lt;/code&gt; fires, it triggers the Daily “app-message” event. Our &lt;code&gt;handleAppMessage&lt;/code&gt; event listener reacts to every "app-message." The handler gets the name of the sender from the Daily call object, and updates recipients’ chat histories using &lt;code&gt;setChatHistory.&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;participants&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;callObject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt;
       &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fromId&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt;
       &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
     &lt;span class="nx"&gt;setChatHistory&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
       &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;chatHistory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="na"&gt;sender&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
     &lt;span class="p"&gt;]);&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;After updating state, the function calls the &lt;code&gt;props.notification()&lt;/code&gt; passed down from the parent Tray.js component to highlight the recipient’s chat icon. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hM6UXQB8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jr7pf5zga0btbjzdpncv.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hM6UXQB8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/jr7pf5zga0btbjzdpncv.gif" alt="chat icon flashes red and grey" width="350" height="86"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We added that chat icon in Tray.js, where we pass state booleans and controller functions as props to Chat.js that determine whether the icon is highlighted or the chat window should be displayed (if it’s been clicked).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;displayChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setChatDisplay&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;highlightedChat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setChatHighlight&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;toggleChat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;setChatDisplay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;displayChat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;highlightedChat&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;setChatHighlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;highlightedChat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;handleNewChat&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="nx"&gt;setChatHighlight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;highlightedChat&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Relevant component snippets &lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TrayButton&lt;/span&gt;
       &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;TYPE_CHAT&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;disabled&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="nx"&gt;highlighted&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;highlightedChat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;toggleChat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Chat&lt;/span&gt; &lt;span class="nx"&gt;onClickDisplay&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;displayChat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;notification&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleNewChat&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Want to chat more?
&lt;/h2&gt;

&lt;p&gt;There are so many ways to keep customizing Daily calls. While we hope this post is a good springboard for adding text chat to your video apps, you might be interested in more advanced functionality, like enabling file exchange or a chat history that persists once a call ends. A third-party chat integration would be the best way to do that. Stay tuned for more!&lt;/p&gt;

&lt;p&gt;For now, experiment with &lt;a href="https://github.com/daily-demos/call-object-react"&gt;our repository&lt;/a&gt;, and please send feedback our way any time at &lt;code&gt;help@daily.co&lt;/code&gt;. &lt;/p&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 &lt;a href="https://reactjs.org/docs/hooks-intro.html"&gt;more on hooks&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>react</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Add a text or image overlay to a video element</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Thu, 03 Dec 2020 18:44:48 +0000</pubDate>
      <link>https://dev.to/trydaily/add-a-text-or-image-overlay-to-a-video-element-3ecg</link>
      <guid>https://dev.to/trydaily/add-a-text-or-image-overlay-to-a-video-element-3ecg</guid>
      <description>&lt;p&gt;We talk to a lot of developers building video calls at &lt;a href="https://dev.to/trydaily"&gt;Daily&lt;/a&gt;, and one thing they often want to do is overlay text (like a participant’s name) or small images (muted state indicators or logos) on top of a video element. This post walks through how to do that! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--yd3KfTm8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/wgyx7vhqeqteq6w4ajo9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--yd3KfTm8--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/wgyx7vhqeqteq6w4ajo9.png" alt="Person in Daily call points to their name at the top left corner of the video stream" width="800" height="464"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;It took me more time to take this screenshot with one hand than it took me to add my name.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;First, we’ll cover the foundational CSS for positioning one element on top of another. Then, we’ll apply that CSS and build on top of Paul’s &lt;a href="https://dev.to/trydaily/build-a-video-chat-app-in-minutes-with-react-and-daily-js-481c"&gt;React video chat app tutorial&lt;/a&gt;. &lt;/p&gt;
&lt;h2&gt;
  
  
  Arrange and stack elements with CSS
&lt;/h2&gt;

&lt;p&gt;We’ll set &lt;code&gt;position&lt;/code&gt; and &lt;code&gt;z-index&lt;/code&gt; properties to arrange our elements. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/position"&gt;&lt;code&gt;position&lt;/code&gt;&lt;/a&gt; gives us control of how an element will sit in the overall layout of the page. When the property is not set, every block-level HTML element appears on a new line [0]. We don’t want that! We specifically want our name tag directly on top of and overlapping our video container. Where the name tag goes depends on the video's position.&lt;/p&gt;

&lt;p&gt;To set up this dependent relationship, we set our video's &lt;code&gt;position&lt;/code&gt; property to &lt;code&gt;relative&lt;/code&gt;. Then, we can arrange any child elements, in our case our name tag, in relation to it by &lt;a href="https://css-tricks.com/absolute-positioning-inside-relative-positioning/"&gt;setting their &lt;code&gt;position&lt;/code&gt; property to &lt;code&gt;absolute&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To see this in action, experiment with removing &lt;code&gt;position:relative&lt;/code&gt; from the &lt;code&gt;.parent-container&lt;/code&gt; class in this codepen:&lt;/p&gt;

&lt;p&gt;&lt;iframe height="600" src="https://codepen.io/kimberlee/embed/WNxWWZQ?height=600&amp;amp;default-tab=result&amp;amp;embed-version=2"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;Our boxes’ &lt;code&gt;top&lt;/code&gt;, &lt;code&gt;bottom&lt;/code&gt;, &lt;code&gt;right&lt;/code&gt;, and &lt;code&gt;left&lt;/code&gt; properties offset them relative to &lt;code&gt;.parent-container&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;With the dependent relationship established, it's time to move on to stacking elements. To do that, we'll need the &lt;code&gt;z-index&lt;/code&gt; property. Because we set &lt;code&gt;position&lt;/code&gt; properties, we can make use of &lt;code&gt;z-index&lt;/code&gt; to stack our elements. The higher the &lt;code&gt;z-index&lt;/code&gt; number, the closer to the screen the element will be. Swap the &lt;code&gt;.red-box&lt;/code&gt; and &lt;code&gt;.green-box&lt;/code&gt; &lt;code&gt;z-index&lt;/code&gt; values in the codepen to see what I mean. &lt;/p&gt;

&lt;p&gt;We now know how to arrange child elements in relation to their parents using &lt;code&gt;position&lt;/code&gt;, and how to stack them with &lt;code&gt;z-index&lt;/code&gt;. We’re ready to take those concepts over to our &lt;a href="https://dev.to/trydaily/build-a-video-chat-app-in-minutes-with-react-and-daily-js-481c"&gt;React video chat app&lt;/a&gt;, but first let’s look at how we can get participant names from the &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface"&gt;Daily call object&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Passing participant names as props in React
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface"&gt;The Daily call object&lt;/a&gt; keeps track of our call state, meaning important information about the meeting. This includes details like other participants (e.g. their audio and video tracks and user_name) and the things they do on the call (e.g. muting their mic or leaving)[1]. The call object also provides methods for interacting with the meeting.&lt;/p&gt;

&lt;p&gt;In our &lt;a href="https://github.com/daily-demos/call-object-react"&gt;demo app&lt;/a&gt;, we map the Daily call object state to a corresponding component state called &lt;code&gt;callItems&lt;/code&gt; in &lt;code&gt;callState.js&lt;/code&gt;. Each call item represents a participant, and contains their audio and video tracks, along with a boolean state indicator about whether or not their call is loading. To also track participant names, we'll add &lt;code&gt;participantName&lt;/code&gt; to each call item.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialCallState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;local&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;audioTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;participantName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="na"&gt;clickAllowTimeoutFired&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;camOrMicError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="na"&gt;fatalError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to add &lt;code&gt;participantName&lt;/code&gt; to our &lt;code&gt;getCallItems&lt;/code&gt; function as well. This function loops over the call object to populate our &lt;code&gt;callItems&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getCallItems&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prevCallItems&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;callItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;initialCallState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callItems&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// Ensure we *always* have a local participant&lt;/span&gt;
 &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participants&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="c1"&gt;// Here we assume that a participant will join with audio/video enabled.&lt;/span&gt;
   &lt;span class="c1"&gt;// This assumption lets us show a "loading" state before we receive audio/video tracks.&lt;/span&gt;
   &lt;span class="c1"&gt;// This may not be true for all apps, but the call object doesn't yet support distinguishing&lt;/span&gt;
   &lt;span class="c1"&gt;// between cases where audio/video are missing because they're still loading or muted.&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prevCallItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;prevCallItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;missingTracks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioTrack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
   &lt;span class="nx"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;hasLoaded&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;missingTracks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;audioTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioTrack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="na"&gt;participantName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user_name&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Guest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenVideoTrack&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenAudioTrack&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="nx"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-screen&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenVideoTrack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;audioTrack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;participant&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;screenAudioTrack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
     &lt;span class="p"&gt;};&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;getCallItems gets called in Call.js [2]. It then passes the callItems as props via the getTiles function  to &lt;code&gt;&amp;lt;Tile&amp;gt;&lt;/code&gt;, the component that displays each participant. We’ll add &lt;code&gt;participantName&lt;/code&gt; to the list of props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Call&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// Lots of other things happen here! See our demo for full code.&lt;/span&gt;
&lt;span class="c1"&gt;//&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getTiles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;largeTiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;smallTiles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
   &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callItem&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLarge&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
       &lt;span class="nx"&gt;isScreenShare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt;
       &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;containsScreenShare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;callState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;callItems&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
     &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Tile&lt;/span&gt;
         &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;videoTrack&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;callItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoTrack&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;audioTrack&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;callItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;audioTrack&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;isLocalPerson&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
         &lt;span class="nx"&gt;isLarge&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;isLarge&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;callItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;participantName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;callItem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nx"&gt;isLocal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
             &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;
             &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                 &lt;span class="nx"&gt;sendHello&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLarge&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;largeTiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nx"&gt;smallTiles&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tile&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
     &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;});&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;largeTiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;smallTiles&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;largeTiles&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;smallTiles&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;getTiles&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;call&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;large-tiles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
         &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;
           &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;largeTiles&lt;/span&gt;
           &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="cm"&gt;/* Avoid showing large tiles to make room for the message */&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;small-tiles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;smallTiles&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;CallMessage&lt;/span&gt;
         &lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;detail&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
         &lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isError&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;)}&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, in Tile.js, we display the name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;Tile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// More code&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;getParticipantName&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantName&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;participant-name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;participantName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;

 &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getClassNames&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;background&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getLoadingComponent&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getVideoComponent&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getAudioComponent&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;getParticipantName&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And style it using familiar CSS in Tile.css, with our container tiles set to relative positioning and our video streams and name tags set to &lt;code&gt;absolute&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.tile.small&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;200px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="m"&gt;10px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.tile.large&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;margin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.tile&lt;/span&gt; &lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.participant-name&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="nl"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#ffffff&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;'Helvetica Neue'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-style&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-weight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;normal&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;font-size&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1rem&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;line-height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;13px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#4a4a4a&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
 &lt;span class="nl"&gt;z-index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And there you have it!&lt;/p&gt;

&lt;p&gt;If you have any questions or feedback about this post, please email me any time at &lt;a href="mailto:kimberlee@daily.co"&gt;kimberlee@daily.co&lt;/a&gt;. Or, if you’re looking to explore even more ways to customize Daily calls, &lt;a href="https://docs.daily.co/reference#introduction"&gt;explore our docs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[0] This is not the case for &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Inline_elements"&gt;inline elements&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;[1] A participant’s &lt;code&gt;user_name&lt;/code&gt; can be set in a few different ways. It can be passed as a &lt;a href="https://docs.daily.co/reference#the-dailyiframe-class"&gt;property&lt;/a&gt; to the DailyIframe, or &lt;a href="https://docs.daily.co/reference#meeting-tokens"&gt;set with a meeting token&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;[2] More specifically, any time there’s a change to participants on the call, Call.js dispatches an action to a reducer that updates state via &lt;code&gt;getCallItems&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>react</category>
      <category>video</category>
      <category>daily</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Monitor how code performance impacts video call quality</title>
      <dc:creator>Kimberlee Johnson </dc:creator>
      <pubDate>Mon, 16 Nov 2020 18:43:09 +0000</pubDate>
      <link>https://dev.to/trydaily/monitor-how-code-performance-impacts-video-call-quality-1cnc</link>
      <guid>https://dev.to/trydaily/monitor-how-code-performance-impacts-video-call-quality-1cnc</guid>
      <description>&lt;h1&gt;
  
  
  Add a stats.js bookmarklet to your video app for immediate insights
&lt;/h1&gt;

&lt;p&gt;If you’re reading this post in 2020, then the odds are high that you’ve been on a lot of video calls this year. And that means that more than once you’ve probably experienced the frustrations that accompany low quality video calls: delayed audio or video, blurry screens, patchy connections, to name a few. In order to avoid these hiccups, it’s useful to monitor call performance to improve video apps.&lt;/p&gt;

&lt;p&gt;Developers building with our Daily video chat APIs can use our &lt;a href="https://docs.daily.co/docs#ways-to-build-with-daily"&gt;prebuilt UI&lt;/a&gt; or build their own &lt;a href="https://docs.daily.co/docs/build-a-custom-video-chat-interface"&gt;custom video chat layout&lt;/a&gt; on top of the Daily call object. Both options support ways to gain insights into call performance.  &lt;/p&gt;

&lt;p&gt;We built a network tab in the &lt;a href="https://www.daily.co/blog/prebuilt-ui/"&gt;Daily prebuilt UI&lt;/a&gt; to show how the video chat adapts to the local network conditions. Daily calls work well even on networks with limited bandwidth and high packet loss, and the network tab gives users some insight into what's happening under the hood. Call participants can see packet loss, download rate, and upload rate.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--06U_mnWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/l3555ezxpiqtnarel4x9.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--06U_mnWT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/l3555ezxpiqtnarel4x9.jpg" alt="Network tab displays call stats on a video call" width="800" height="874"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re a developer building on top of the Daily call object, you can monitor for &lt;a href="https://docs.daily.co/reference#network-events"&gt;several network events&lt;/a&gt; and build your own monitoring panel.  &lt;/p&gt;

&lt;p&gt;While we built these tools for monitoring network performance, we’re also often curious about how the performance of the code in the demo apps we build might impact the quality of our calls. We realized other developers building on Daily might also be asking this question.&lt;/p&gt;

&lt;p&gt;Enter &lt;a href="http://mrdoob.github.io/stats.js/"&gt;stats.js&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s---V3GLh6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/0cpfe78c9jv7mrkvkhf5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s---V3GLh6V--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/0cpfe78c9jv7mrkvkhf5.png" alt="Neon panels display call stats" width="686" height="125"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Stats.js is a JavaScript performance monitor that tracks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frames per second (FPS): a low number means there’s something we should change.&lt;/li&gt;
&lt;li&gt;Milliseconds needed to render a frame (MS): a high number signals we should make some updates.&lt;/li&gt;
&lt;li&gt;MBytes of allocated memory (MB): Run Chrome with --enable-precise-memory-info.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;To use, create a new bookmark, saving this bookmarklet as the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;:(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onload&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Stats&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dom&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;(){&lt;/span&gt;&lt;span class="nx"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="nx"&gt;requestAnimationFrame&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loop&lt;/span&gt;&lt;span class="p"&gt;)});};&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;//mrdoob.github.io/stats.js/build/stats.min.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;head&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;appendChild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;script&lt;/span&gt;&lt;span class="p"&gt;);})()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, navigate to your web app, and click on the bookmark. When you do, the panes will display. You can click through to see the different stats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FEIFgtVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/5wcyy6chjqpff56oinpu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FEIFgtVd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/i/5wcyy6chjqpff56oinpu.gif" alt="Click rotates through different call panes" width="158" height="118"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you test this out in your projects, &lt;a href="https://www.daily.co/contact#:~:text=Send%20us%20an%20email%20anytime,Interested%20in%20joining%20our%20team%3F"&gt;let us know&lt;/a&gt; what you think! In the meantime, stay tuned for more updates about monitoring Daily call performance.&lt;/p&gt;

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