2019-11-07
2580
#react
Andrew James
9113
Nov 7, 2019 ⋅ 9 min read

Building a responsive camera component with React Hooks

Andrew James Frontend Engineer @ Coinbase

Recent posts:

Rxjs Adoption Guide: Overview, Examples, And Alternatives

RxJS adoption guide: Overview, examples, and alternatives

Get to know RxJS features, benefits, and more to help you understand what it is, how it works, and why you should use it.

Emmanuel Odioko
Jul 26, 2024 ⋅ 13 min read
Decoupling Monoliths Into Microservices With Feature Flags

Decoupling monoliths into microservices with feature flags

Explore how to effectively break down a monolithic application into microservices using feature flags and Flagsmith.

Kayode Adeniyi
Jul 25, 2024 ⋅ 10 min read
Lots of multi-colored blue and purplish rectangles.

Animating dialog and popover elements with CSS @starting-style

Native dialog and popover elements have their own well-defined roles in modern-day frontend web development. Dialog elements are known to […]

Rahul Chhodde
Jul 24, 2024 ⋅ 10 min read
Using Llama Index To Add Personal Data To Large Language Models

Using LlamaIndex to add personal data to LLMs

LlamaIndex provides tools for ingesting, processing, and implementing complex query workflows that combine data access with LLM prompting.

Ukeje Goodness
Jul 23, 2024 ⋅ 5 min read
View all posts

7 Replies to "Building a responsive camera component with React Hooks"

  1. This was really helpful in learning. I noticed that the main camera component is capitalized, but the supporting functions like useUserMedia are not. As a best practice where should those supporting functions live? Guessing not in a folder like Camera would inside of a Components folder.

  2. Super helpful, thank you for posting this.

    However your useUserMedia wasn’t working properly for me, so I refactored it like this:

    import { useState, useEffect } from “react”;

    export function useUserMedia(requestedMedia) {
    const [mediaStream, setMediaStream] = useState(null);

    useEffect(() => {
    // Creating reference at the top of useEffect’s scope for cleanup later
    let streamRef = null;

    async function enableStream() {
    try {
    // Using ref instead of a new const variable
    streamRef = await navigator.mediaDevices.getUserMedia(requestedMedia);
    setMediaStream(streamRef);
    } catch(err) {
    // Removed for brevity
    }
    }

    if (!mediaStream) {
    enableStream();
    }

    // Removed else block
    return function cleanup() {
    // Now the ref can be used to stop all media tracks
    streamRef.getTracks().forEach(track => {
    track.stop();
    });
    }

    // Removed dependency array
    }, []);

    return mediaStream;

    Now when the component unmounts, the camera stops recording.

    1. I had a similar issue, but in dev mode the cleanup function was running before the getUseMedia promise resolved, so the streamRef was null when cleanup ran and the MediaStream wasn’t closed. This meant that when I clicked “Take a picture”, the webcam stayed on.

      The way I got around this was by using a streamRefArray outside of the useEffect and pushing each new stream into that instead of reassigning the the streamRef variable. Then, on cleanup, I’d loop through the streamRefArray MediaStreams and stop all their tracks. This seemed to do the trick.

      import { useState, useEffect } from “react”

      export function useUserMedia(requestedMedia: MediaStreamConstraints) {
      const [mediaStream, setMediaStream] = useState(null)
      let streamRefArray: MediaStream[] = []

      useEffect(() => {
      async function enableStream() {
      try {
      const stream = await navigator.mediaDevices.getUserMedia(requestedMedia)
      // Push media stream to reference array
      // to stop track in later cleanup
      streamRefArray.push(stream)
      setMediaStream(stream)
      } catch (err) {
      console.error(err)
      }
      }

      if (!mediaStream) {
      enableStream()
      }

      return function cleanup() {
      // Loop through array of media streams
      streamRefArray.forEach((stream) => {
      stream?.getTracks().forEach((track) => {
      console.log(track)
      track.stop()
      })
      streamRefArray = []
      })
      }
      }, [])

      return mediaStream
      }

  3. Hi Andrew,
    Can you please shed some light on how to resize the container for mobile devices, as for the mobile devices i need height > width.
    Thanks in Advance.

Leave a Reply