ReactJs:useRef 当前变空

ReactJs : useRef current getting null

我正在尝试关闭 相机和手电筒 当组件获得 unmount 时,我正在使用 react hook 我有两个功能 startCamera() 和 stopCamera() ,我在组件安装时调用 startcamera 并在组件卸载时停止相机。 但是在卸载时调用 stopCamera 时它向我显示错误

我创建了另一个按钮来测试 StopCamera() 是否正常工作,我发现 它正常工作但我想在组件获取时调用该函数卸载

我的代码:

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:以上代码工作于:https://5t2to.csb.app/

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

您可以使用 useLayoutEffect 挂钩。它在卸载之前工作,例如 componentWillUnmount.

这是一个例子 https://codesandbox.io/s/happy-swartz-ikqdn?file=/src/random.js

您可以转到 sandbox browser 中的 https://ikqdn.csb.app/rand 并在单击 to home 按钮时检查 console

您可以看到 useEffectuseLayoutEffect

unmounting 工作时的区别

它保留了 ref.current,所以你可以做的是,你可以在调用 just before unmounting 的函数中传递 ref.current,以防止引用 dom elment ].

花了点时间 debugging/sleuthing 才找到问题。因此,即使你有一个 ref 附加到 video 元素,当组件被卸载时,ref 仍然会发生变化并变得未定义。解决方案是保存对当前 videoElement 引用值的引用,并在清理函数中使用它。

useEffect(() => {
  startCamera();

  const ref = videoElement.current;

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

只需添加 useLayoutEffect 即可停止摄像头

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();
},[]);

只需要将 useEffect() 更改为 useLayoutEffect() 就可以了。

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

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