DEV Community

Cover image for Learn React Hooks by looking under the hood of a video chat app
Kimberlee Johnson
Kimberlee Johnson

Posted on • Edited on

Learn React Hooks by looking under the hood of a video chat app

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!

I really like getting to immediately apply 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 Daily React video chat demo.

When I think about the roles useEffect, useMemo, and useCallback 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.

Dustin Hoffman smiles as Captain Hook

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 demo app, and why we decided to use each.

I mentioned in my last post about avoiding global variables that I'm trying to get better about sharing what my colleagues teach me. This round is brought to us by the inimitable Jess Mitchell, who I am so grateful to work with and learn from!

Before we get hook'd

I really liked Ali Spittel's definition of Hooks on the latest Ladybug podcast:

"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."

This means we can do unique things when components mount, update, and unmount. Like the docs say, we can take advantage of state and other features without having to write class components.

With that overview in mind, let's look at three Hooks in our video chat app: useEffect, useMemo, and useCallback.

useEffect to manage participant updates in state

With useEffect, we can, well, perform side effects in function components, based on state or prop changes.

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.

The Daily API fires corresponding events as these things happen, e.g. 'participant-joined', 'track-stopped', etc. In our video chat app, we listen for these events and their handlers set our particpantUpdated state in response.

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

useEffect(() => {
    if (participantUpdated) {
      const list = Object.values(callObject?.participants());
      setParticipants(list);
    }
  }, [participantUpdated, callObject]);
Enter fullscreen mode Exit fullscreen mode

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.

useMemo to re-render videos only when we have to

useMemo returns a memoized value. Memoized means a value that's the result of an expensive function call.

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.

Instead, we pass our participants state value as a dependency to the useMemo hook that displays our tiles.

const displayLargeTiles = useMemo(() => {
    const isLarge = true;
    const tiles = participants?.filter((p) => !p.local);
     return (
        <div className="large-tiles">
          {tiles?.map((t, i) => (
            <Tile
              key={`large-${i}`}
              videoTrackState={t?.tracks?.video}
              audioTrackState={t?.tracks?.audio}
              isLarge={isLarge}
              disableCornerMessage={isScreenShare}
              onClick={
                t.local
                  ? null
                  : () => {
                      sendHello(t.id);
                    }
              }
            />
          ))}
        </div>
      );
    }
  }, [participants]);
Enter fullscreen mode Exit fullscreen mode

The useMemo hook lets us only change the videos displayed when the participants have changed, instead of recalculating on every render.

Note: Because we built a video chat app with participant changes that happen often, the performance benefits fromuseMemo are significant; it saves us many recalculations. In a lot of cases, though, this hook might be more than your application needs. useMemo has a reputation of being overused, so tread with caution.

useCallback to re-render startLeavingCall() function only when we have to

Just like useMemo prevents us from re-calculating values that haven't changed, useCallback lets us stop specific functions from re-rendering.

In our App.js component, lots of things can trigger a re-render. But our startLeavingCall function, for example, only needs to re-render if the callObject, which stores data about our call, or our appState changes. This is because the function does different things depending on those values.

We pass callObject and appState as our dependencies.

/**
   * Starts leaving the current call.
   */
  const startLeavingCall = useCallback(() => {
    if (!callObject) return;
    // If we're in the error state, we've already "left", so just clean up
    if (appState === STATE_ERROR) {
      callObject.destroy().then(() => {
        setRoomUrl(null);
        setCallObject(null);
        setAppState(STATE_IDLE);
      });
    } else {
      setAppState(STATE_LEAVING);
      callObject.leave();
    }
  }, [callObject, appState]);
Enter fullscreen mode Exit fullscreen mode

Hook'd and wanting more?

Rufio from the movie Hook

I hope this helped make Hooks feel a little more applicable! Can you think of any new ways to apply useEffect or useMemo, or useCallback in apps that you're building? Tell me in the comments! Especially tell me if you'll be building any video (or audio!) apps. You can give me a shout over on Twitter too!

Top comments (11)

Collapse
 
andrewbaisden profile image
Andrew Baisden

Nice post.

Collapse
 
kimberleejohnson profile image
Kimberlee Johnson

Thank you!

Collapse
 
ajest profile image
Pablo Fumarola

Good post! It helped me a lot

Collapse
 
kimberleejohnson profile image
Kimberlee Johnson

I'm so glad to hear that, thank you, @ajest ! @gemontracks helped me a lot and I'm happy to pass it on.

Collapse
 
nguyenak95 profile image
Nguyen An Khang

I had built a telehealth app with the same logic as above. If I read this post at that time, things could be easier, lol. But the process of finding a solution is fun :)

Collapse
 
kimberleejohnson profile image
Kimberlee Johnson

Solutions-finding is what it's all about, and we need all the telehealth solutions we can get these days! It's awesome that you built one ๐Ÿ’ฏ

Collapse
 
beraliv profile image
beraliv

Please update the link to Twitter, it's not working

Collapse
 
kimberleejohnson profile image
Kimberlee Johnson

Thank you for letting me know! Sorry about that. I just updated that link, and here it is too in case useful: twitter.com/kimeejohnson

Collapse
 
beraliv profile image
beraliv

Great! Thank you for the quick response!

Collapse
 
alanmaranto profile image
alanmaranto

Nice! Thank U!

Collapse
 
kimberleejohnson profile image
Kimberlee Johnson

Thank you so much for reading!