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:

6 React Server Component performance pitfalls in Next.js

6 React Server Component performance pitfalls in Next.js

React Server Components and the Next.js App Router enable streaming and smaller client bundles, but only when used correctly. This article explores six common mistakes that block streaming, bloat hydration, and create stale UI in production.

Temitope Oyedele
Feb 23, 2026 ⋅ 13 min read
podrocket 2 19

Making sense of web rendering patterns (SSR, CSR, static, islands)

Gil Fink (SparXis CEO) joins PodRocket to break down today’s most common web rendering patterns: SSR, CSR, static rednering, and islands/resumability.

PodRocket
Feb 23, 2026 ⋅ 48 sec read

CSS @container scroll-state: Replace JS scroll listeners now

CSS @container scroll-state lets you build sticky headers, snapping carousels, and scroll indicators without JavaScript. Here’s how to replace scroll listeners with clean, declarative state queries.

Jude Miracle
Feb 19, 2026 ⋅ 4 min read
Anti-libraryism 10 web APIs that replace modern JavaScript libraries

Anti-libraryism: 10 web APIs that replace modern JavaScript libraries

Explore 10 Web APIs that replace common JavaScript libraries and reduce npm dependencies, bundle size, and performance overhead.

Chizaram Ken
Feb 19, 2026 ⋅ 15 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

Hey there, want to help make our blog better?

Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now