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:

ai dev tool power rankings

AI dev tool power rankings & comparison [July 2025 edition]

Which AI frontend dev tool reigns supreme in July 2025? Check out our power rankings and use our interactive comparison tool to find out.

Chizaram Ken
Jul 2, 2025 ⋅ 3 min read
how API client automation can save you hours in development

How API client automation can save you hours in development

Learn how OpenAPI can automate API client generation to save time, reduce bugs, and streamline how your frontend app talks to backend APIs.

Lewis Cianci
Jul 1, 2025 ⋅ 7 min read
Interface Segregation Principle

SOLID series: Understanding the Interface Segregation Principle (ISP)

Discover how the Interface Segregation Principle (ISP) keeps your code lean, modular, and maintainable using real-world analogies and practical examples.

Oyinkansola Awosan
Jun 30, 2025 ⋅ 7 min read
​​How HTML’s Selectedcontent Element Improves Dropdowns

​​How HTML’s <selectedcontent> element improves dropdowns

is an experimental HTML element that gives developers control over how a selected option is displayed, using just HTML and CSS.

Temitope Oyedele
Jun 27, 2025 ⋅ 6 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