useQRScanner
提供 Telegram 原生 QR 扫码功能的 Hook,让你的 Mini App 可以扫描二维码和条形码。
导入
typescript
import { useQRScanner } from '@xcloud/ui-telegram';示例
API
返回值
| 属性 | 类型 | 描述 |
|---|---|---|
| open | (text?: string) => Promise<string | null> | 打开 QR 扫描器 |
| close | () => void | 关闭 QR 扫描器 |
| isOpen | boolean | 扫描器是否正在打开 |
| isAvailable | () => boolean | 检查 QR 扫描器是否可用 |
参数
open(text?)
| 参数 | 类型 | 必填 | 描述 |
|---|---|---|---|
| text | string | 否 | 扫描器显示的提示文本 |
返回值: Promise<string | null>
- 成功扫描返回二维码内容
- 用户取消返回
null
使用示例
基础扫描
最简单的扫描使用方式:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
function ScanButton() {
const { open } = useQRScanner();
const handleScan = async () => {
const data = await open('扫描二维码');
if (data) {
console.log('扫描结果:', data);
} else {
console.log('用户取消了扫描');
}
};
return <button onClick={handleScan}>扫描二维码</button>;
}支付扫码
扫描支付二维码:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useInvoice } from '@xcloud/ui-telegram';
function PaymentScanner() {
const { open } = useQRScanner();
const { openInvoice } = useInvoice();
const handleScanPayment = async () => {
const paymentUrl = await open('扫描支付二维码');
if (paymentUrl) {
// 验证是否为有效的支付链接
if (paymentUrl.startsWith('https://t.me/$')) {
await openInvoice({ url: paymentUrl });
} else {
alert('无效的支付二维码');
}
}
};
return (
<button onClick={handleScanPayment}>
扫码支付
</button>
);
}网站跳转
扫描网站二维码并跳转:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
function WebsiteScanner() {
const { open } = useQRScanner();
const handleScanWebsite = async () => {
const url = await open('扫描网站二维码');
if (url) {
// 验证是否为有效URL
try {
const parsedUrl = new URL(url);
// 在新标签页打开
window.open(url, '_blank');
} catch {
alert('扫描到的不是有效的网址');
}
}
};
return <button onClick={handleScanWebsite}>扫描网站</button>;
}添加好友
扫描好友分享码:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
function AddFriendScanner() {
const { open } = useQRScanner();
const handleScanFriend = async () => {
const friendCode = await open('扫描好友分享码');
if (friendCode) {
// 解析好友码并添加好友
const userId = parseFriendCode(friendCode);
await addFriend(userId);
alert('已添加好友!');
}
};
return <button onClick={handleScanFriend}>扫码加好友</button>;
}优惠券扫码
扫描优惠券码并领取:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useState } from 'react';
function CouponScanner() {
const { open, isOpen } = useQRScanner();
const [coupon, setCoupon] = useState<string | null>(null);
const handleScanCoupon = async () => {
const couponCode = await open('扫描优惠券二维码');
if (couponCode) {
try {
// 验证并领取优惠券
const result = await claimCoupon(couponCode);
setCoupon(result.couponName);
alert(`成功领取优惠券:${result.couponName}`);
} catch (error) {
alert('优惠券已失效或已领取');
}
}
};
return (
<div>
<button onClick={handleScanCoupon} disabled={isOpen}>
{isOpen ? '扫描中...' : '扫描优惠券'}
</button>
{coupon && <p>已领取: {coupon}</p>}
</div>
);
}产品扫码
扫描产品条形码查看详情:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useNavigate } from '@tanstack/react-router';
function ProductScanner() {
const { open } = useQRScanner();
const navigate = useNavigate();
const handleScanProduct = async () => {
const barcode = await open('扫描产品条形码');
if (barcode) {
// 跳转到产品详情页
navigate({ to: '/product/$id', params: { id: barcode } });
}
};
return <button onClick={handleScanProduct}>扫描产品</button>;
}批量扫描
连续扫描多个二维码:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useState } from 'react';
function BatchScanner() {
const { open } = useQRScanner();
const [scannedItems, setScannedItems] = useState<string[]>([]);
const [isScanning, setIsScanning] = useState(false);
const handleBatchScan = async () => {
setIsScanning(true);
while (isScanning) {
const data = await open('扫描下一个二维码 (取消停止)');
if (!data) {
// 用户取消,停止扫描
break;
}
setScannedItems((prev) => [...prev, data]);
}
setIsScanning(false);
};
return (
<div>
<button onClick={handleBatchScan}>
{isScanning ? '扫描中...' : '批量扫描'}
</button>
{scannedItems.length > 0 && (
<ul>
{scannedItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
)}
</div>
);
}验证扫描结果
扫描前后进行验证:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useState } from 'react';
function ValidatedScanner() {
const { open } = useQRScanner();
const [error, setError] = useState<string>('');
const handleScan = async () => {
setError('');
const data = await open('扫描二维码');
if (!data) {
return; // 用户取消
}
// 验证扫描结果
if (!isValidQRCode(data)) {
setError('无效的二维码格式');
return;
}
// 处理有效的扫描结果
processQRCode(data);
};
const isValidQRCode = (data: string): boolean => {
// 自定义验证逻辑
return data.startsWith('myapp://');
};
return (
<div>
<button onClick={handleScan}>扫描验证</button>
{error && <p style={{ color: 'red' }}>{error}</p>}
</div>
);
}带加载状态
显示扫描状态和加载动画:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useState } from 'react';
function ScannerWithLoading() {
const { open, isOpen } = useQRScanner();
const [processing, setProcessing] = useState(false);
const handleScan = async () => {
const data = await open('扫描二维码');
if (data) {
setProcessing(true);
try {
// 处理扫描结果(如上传到服务器)
await processData(data);
alert('处理成功!');
} catch (error) {
alert('处理失败');
} finally {
setProcessing(false);
}
}
};
const isLoading = isOpen || processing;
return (
<button onClick={handleScan} disabled={isLoading}>
{isOpen && '扫描中...'}
{processing && '处理中...'}
{!isLoading && '扫描二维码'}
</button>
);
}检查可用性
在不支持的环境中提供替代方案:
tsx
import { useQRScanner } from '@xcloud/ui-telegram';
import { useState } from 'react';
function FlexibleScanner() {
const { open, isAvailable } = useQRScanner();
const [manualInput, setManualInput] = useState('');
const handleScan = async () => {
if (isAvailable()) {
const data = await open('扫描二维码');
if (data) {
processData(data);
}
} else {
// 降级到手动输入
alert('请手动输入二维码内容');
}
};
return (
<div>
{isAvailable() ? (
<button onClick={handleScan}>扫描二维码</button>
) : (
<div>
<input
value={manualInput}
onChange={(e) => setManualInput(e.target.value)}
placeholder="请输入二维码内容"
/>
<button onClick={() => processData(manualInput)}>提交</button>
</div>
)}
</div>
);
}最佳实践
1. 提供清晰的提示文本
告诉用户他们需要扫描什么类型的二维码:
tsx
// ✅ 好的做法
await open('扫描支付二维码');
await open('扫描产品条形码');
await open('扫描会员卡');
// ❌ 不好的做法
await open(); // 没有提示
await open('扫描'); // 提示太模糊2. 处理取消情况
始终检查返回值是否为 null:
tsx
const data = await open('扫描二维码');
if (!data) {
// 用户取消了扫描,不做任何操作
return;
}
// 处理扫描结果
processData(data);3. 验证扫描结果
不要盲目信任扫描到的数据:
tsx
const data = await open('扫描二维码');
if (data) {
// 验证数据格式
if (isValidFormat(data)) {
await processData(data);
} else {
showError('无效的二维码');
}
}4. 提供错误处理
扫描后的处理可能失败:
tsx
const data = await open('扫描二维码');
if (data) {
try {
await processData(data);
} catch (error) {
showError('处理失败,请重试');
}
}5. 显示加载状态
让用户知道当前状态:
tsx
const { open, isOpen } = useQRScanner();
return (
<button onClick={handleScan} disabled={isOpen}>
{isOpen ? '扫描中...' : '扫描二维码'}
</button>
);扫描内容类型
QR 扫描器可以识别多种类型的内容:
网址
https://example.com
http://example.com/pageTelegram 链接
https://t.me/username
https://t.me/joinchat/xxx
https://t.me/$invoice_xxx自定义协议
myapp://action?param=value
bitcoin:address?amount=0.1纯文本
任意文本内容
验证码: 123456
优惠券码: SAVE20JSON 数据
json
{"type": "product", "id": "12345"}注意事项
- QR 扫描功能仅在 Telegram 移动端可用
- 桌面版和 Web 版不支持此功能,使用前应检查
isAvailable() - 用户可以随时取消扫描,务必处理
null返回值 - 扫描过程中会占用摄像头权限
- 不要连续快速调用
open(),等待上一次扫描完成 - 扫描结果的安全性需要在应用层进行验证
相关 API
- useInvoice - 支付功能
- useHapticFeedback - 触觉反馈