React项目中Html5Qrcode组件卸载时无法停止相机的问题
React中Html5Qrcode路由跳转时无法停止相机的解决方案
问题背景
在React应用中使用Html5Qrcode包实现扫码功能,扫码成功后调用html5QrCode.stop()可正常停止扫描,但当用户未完成扫码就进行路由跳转(返回、切换URL等)时,相机仍会在后台持续运行,无视频画面却占用系统资源。
尝试通过componentWillUnmount生命周期处理相机停止逻辑时,出现错误:Uncaught TypeError: Cannot set properties of undefined (setting 'element'),原代码中在componentWillUnmount里重新实例化Html5Qrcode,导致DOM元素已卸载时实例化失败。
错误原因
componentDidMount中创建的html5QrCode是局部变量,组件其他生命周期无法访问,只能重新实例化,但此时DOM元素qr-reader可能已被销毁,实例化失败。- 直接调用
Html5Qrcode("qr-reader")并非正确的实例获取方式,应该复用组件内已创建的实例。
修复步骤
- 在组件类中保存
html5QrCode实例,通过组件实例属性共享,避免局部变量无法访问的问题。 - 在
componentWillUnmount中先判断实例是否存在,再调用stop()方法,确保异步操作的错误被捕获。 - 确保组件卸载时,直接通过已保存的实例停止扫描,无需依赖已销毁的DOM元素。
修复后的完整代码
import React, { Component } from 'react'; import { Html5Qrcode } from 'html5-qrcode'; export default class Scanner extends Component { constructor(props) { super(props); this.state = { availableCameras: [] }; // 初始化实例变量,用于保存Html5Qrcode实例 this.html5QrCode = null; } handleChange(event) { this.setState({ [event.target.name]: event.target.value }); } componentDidMount() { Html5Qrcode.getCameras().then(devices => { this.setState({ availableCameras: devices }); // 将实例赋值给组件属性,方便后续生命周期访问 this.html5QrCode = new Html5Qrcode("qr-reader"); this.html5QrCode.start( { facingMode: "environment" }, { fps: 2, qrbox: 250 }, qrCodeMessage => { // 扫码成功后停止扫描 this.html5QrCode.stop().then(() => { console.log("扫码成功,已停止扫描"); }).catch(err => { console.log("停止扫描失败:", err); }); this.props.returnedInfo(qrCodeMessage); }, errorMessage => { // 忽略常规的扫码失败错误 console.log(`未检测到二维码: ${errorMessage}`); } ).catch(err => { console.log(`启动扫描失败: ${err}`); }); }).catch(error => { console.log("获取相机列表失败:", error); }); } componentWillUnmount() { // 检查实例是否存在,避免空指针错误 if (this.html5QrCode) { this.html5QrCode.stop().then(() => { console.log("组件卸载,已停止相机扫描"); }).catch(error => { console.log("组件卸载时停止扫描失败:", error); }); } } render() { return <div id="qr-reader" />; } }
函数组件版本补充
如果使用React函数组件,可通过useRef保存Html5Qrcode实例,并在useEffect的清理函数中执行停止逻辑:
import React, { useEffect, useRef } from 'react'; import { Html5Qrcode } from 'html5-qrcode'; const Scanner = ({ returnedInfo }) => { const html5QrCodeRef = useRef(null); useEffect(() => { Html5Qrcode.getCameras().then(devices => { html5QrCodeRef.current = new Html5Qrcode("qr-reader"); html5QrCodeRef.current.start( { facingMode: "environment" }, { fps: 2, qrbox: 250 }, qrCodeMessage => { html5QrCodeRef.current.stop().then(() => { console.log("扫码成功,已停止扫描"); }); returnedInfo(qrCodeMessage); }, errorMessage => { console.log(`未检测到二维码: ${errorMessage}`); } ).catch(err => { console.log(`启动扫描失败: ${err}`); }); }).catch(error => { console.log("获取相机列表失败:", error); }); // 组件卸载时停止扫描 return () => { if (html5QrCodeRef.current) { html5QrCodeRef.current.stop().catch(error => { console.log("组件卸载时停止扫描失败:", error); }); } }; }, [returnedInfo]); return <div id="qr-reader" />; }; export default Scanner;
内容的提问来源于stack exchange,提问作者Renta
相关产品推荐
相关产品推荐

