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
。
您可以看到 useEffect
和 useLayoutEffect
在 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
我正在尝试关闭 相机和手电筒 当组件获得 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
。
您可以看到 useEffect
和 useLayoutEffect
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