4

I am trying to turnoff camera and flashlight when the component gets unmount , I am using react hook I have two function startCamera() and stopCamera() , I am calling startcamera when the component gets mount and stop camera when component gets unmount. But its showing me error when stopCamera is called while unmounting enter image description here

i have created an another button to test if StopCamera() working and i found its working , but i want to call the function when component is getting unmounted

my code:

CameraScreen.js


import "./styles.css";
import { useState, useEffect, useRef } from "react";

export default function CameraScreen() {
  const videoElement = useRef(null);
  const [facingMode, setFacingMode] = useState("environment");
  const handleFacingModeToggle = () => {
    stopCamera();
    facingMode === "environment"
      ? setFacingMode("user")
      : setFacingMode("environment");
  };
  useEffect(() => {
    // const getUserMedia = async () => {
    //   try {
    //     const stream = await navigator.mediaDevices.getUserMedia({
    //       video: true
    //     });
    //     videoElement.current.srcObject = stream;
    //   } catch (err) {
    //     console.log(err);
    //   }
    // };
    // getUserMedia();
    startCamera();
    return function cleanup() {
      stopCamera();
    };
  }, []);

  const stopCamera = () =>
    videoElement.current.srcObject &&
    videoElement.current.srcObject.getTracks().forEach((t) => t.stop());
  function startCamera() {
    if (navigator.mediaDevices.getUserMedia) {
      navigator.mediaDevices
        .getUserMedia({
          video: { facingMode: facingMode },
          width: { ideal: 1280 },
          height: { ideal: 720 }
        })
        .then(function (stream) {
          if (videoElement.current) videoElement.current.srcObject = stream;
          const track = stream.getVideoTracks()[0];

          //Create image capture object and get camera capabilities
          const imageCapture = new ImageCapture(track);
          const photoCapabilities = imageCapture
            .getPhotoCapabilities()
            .then(() => {
              //todo: check if camera has a torch

              //let there be light!

              track.applyConstraints({
                advanced: [{ torch: true }]
              });
            });
        })
        .catch(function (error) {
          alert("Please check your device permissions");
          console.log("Something went wrong!");
          console.log(error);
        });
      if (videoElement.current)
        videoElement.current.onloadeddata = function () {
          if (window.NativeDevice)
            window.NativeDevice.htmlCameraReadyToRecord(true);
        };
    }
  }
  return (
    <>
      <video
        autoPlay={true}
        ref={videoElement}
        style={{
          minHeight: "67.82vh",
          maxHeight: "67.82vh",
          maxWidth: "100%",
          minWidth: "100%"
        }}
        className="border-3rem bg-[#666]"
      ></video>

      <button onClick={stopCamera}> stopCamera</button>
    </>
  );
}

App.js

import "./styles.css";
import { useState } from "react";
import CameraScreen from "./cameraScreen";
export default function App() {
  const [switchS, setSwitchS] = useState(false);
  return (
    <div>
      <button className="" onClick={() => setSwitchS(!switchS)} value="switch">
        switch
      </button>
      
      {switchS && <CameraScreen />}
      {!switchS && "Blank Screen"}
    </div>
  );
}

PS: the above code working at :https://5t2to.csb.app/

codesandbox link : https://codesandbox.io/s/practical-fast-5t2to?file=/src/cameraScreen.js

1
  • Change the stopCamera method like this: const stopCamera = () => videoElement?.current?.srcObject && videoElement.current.srcObject.getTracks().forEach((t) => t.stop()); Commented Dec 1, 2021 at 9:48

4 Answers 4

5

You can use useLayoutEffect hook. It works just before unmounting, like componentWillUnmount.

Here is an example to that https://codesandbox.io/s/happy-swartz-ikqdn?file=/src/random.js

You can go to https://ikqdn.csb.app/rand in sandbox browser and check the console on clicking to home button.

You can see the difference in working while unmounting of both useEffect and useLayoutEffect


It preserves ref.current, so what you can do is, you can pass ref.current in the function that you are calling just before unmounting, to prevent ref to the dom elment.

Sign up to request clarification or add additional context in comments.

Comments

1

It took a bit of debugging/sleuthing to find the issue. So even though you have a ref attached to the video element, when the component is unmounted the ref is still mutated and becomes undefined. The solution is to save a reference to the current videoElement ref value and use this in a cleanup function.

useEffect(() => {
  startCamera();

  const ref = videoElement.current;

  return () => {
    ref.srcObject.getTracks().forEach((t) => t.stop());
  };
}, []);

Edit reactjs-useref-current-getting-null

Comments

1

Simply add useLayoutEffect to stop camera

useEffect(() => {
    // const getUserMedia = async () => {
    //   try {
    //     const stream = await navigator.mediaDevices.getUserMedia({
    //       video: true
    //     });
    //     videoElement.current.srcObject = stream;
    //   } catch (err) {
    //     console.log(err);
    //   }
    // };
    // getUserMedia();
    startCamera();
  }, []);
useLayoutEffect(()=>()=>{
   stopCamera();
},[]);

Comments

0

Just need to change useEffect() to useLayoutEffect()and it will works like a charm.

useLayoutEffect(() => {
     const ref = videoElement;
     console.log(ref);
     startCamera();
  return function cleanup() {
     stopCamera();
  };
}, []);

sanbox link :- https://codesandbox.io/s/naughty-murdock-by5tc?file=/src/cameraScreen.js:415-731

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.