使用React qrscan库扫码后无法关闭摄像头重复扫码怎么解决?
问题原因
你使用的qrscan组件未实现卸载时自动释放摄像头媒体流的逻辑,即使通过条件渲染将组件从DOM树移除,后台的摄像头捕获进程仍在运行,绑定的onFind回调会持续被触发,导致重复拿到扫码结果。
解决方案
- 方案1:通过key属性强制组件完全卸载重建
React中当组件的key属性发生变化时,会直接销毁旧的组件实例,触发完整的卸载生命周期,清理组件绑定的所有资源。你只需要在渲染QRScan组件时加入动态key即可:
{scanning ? ( <QRScan key={Date.now()} onFind={onFind} /> ) : ( <> <button onClick={() => setScanning(true)}>Scan</button> <h4>value: {scannerValue}</h4> </> )}
每次打开扫码时生成新的时间戳作为key,关闭扫码时旧的QRScan实例会被完全销毁,摄像头会自动停止。
- 方案2:使用组件内置的启停属性
大部分扫码组件都提供了active/running这类控制扫描状态的属性,不需要通过条件渲染控制组件显示,直接把scanning状态传入对应属性即可:
<QRScan active={scanning} onFind={onFind} /> {!scanning && ( <> <button onClick={() => setScanning(true)}>Scan</button> <h4>value: {scannerValue}</h4> </> )}
组件内部会根据传入的状态自动启停摄像头,不需要卸载组件。
- 方案3:加扫描锁+手动释放摄像头(兜底方案)
如果前两种方案都无效,可以加一个ref标记是否已经拿到扫码结果,同时在拿到结果后手动停止所有摄像头媒体流:
const [scanning, setScanning] = useState(false); const [scannerValue, setScannerValue] = useState(''); // 扫描锁,防止重复触发 const scanLocked = useRef(false); const onFind = (value: string) => { // 已经拿到结果就直接返回 if (scanLocked.current) return; scanLocked.current = true; setScannerValue(value); setScanning(false); console.log(value); // 手动停止所有视频流 navigator.mediaDevices.getUserMedia({ video: true }) .then(stream => { stream.getTracks().forEach(track => track.stop()); }) }; const startScan = () => { // 打开扫码前重置锁 scanLocked.current = false; setScanning(true); }; return ( {scanning ? ( <QRScan onFind={onFind} /> ) : ( <> <button onClick={startScan}>Scan</button> <h4>value: {scannerValue}</h4> </> )} )
内容的提问来源于stack exchange,提问作者Mihail Iliev
相关产品推荐
相关产品推荐

