55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { useEffect, useRef, useState } from 'react'
|
|
import { createBluetoothDiscovery } from '../discovery'
|
|
|
|
// 工厂回调 → React state 的映射产物
|
|
export interface BleUiState {
|
|
searching: boolean
|
|
connected: boolean
|
|
deviceList: Array<{ deviceId: string; name?: string; RSSI?: number }>
|
|
profileId: string
|
|
stage: string
|
|
deviceInfo: Record<string, unknown> | null
|
|
authResult: unknown
|
|
}
|
|
|
|
export function useBle() {
|
|
const [state, setState] = useState<BleUiState>({
|
|
searching: false,
|
|
connected: false,
|
|
deviceList: [],
|
|
profileId: 'UNKNOWN',
|
|
stage: 'IDLE',
|
|
deviceInfo: null,
|
|
authResult: null
|
|
})
|
|
|
|
// discovery 实例在整个组件生命周期内只创建一次;
|
|
// 回调里读最新 state 用 setState 函数式更新,不闭包旧值
|
|
const discoveryRef = useRef<ReturnType<typeof createBluetoothDiscovery> | null>(null)
|
|
if (discoveryRef.current === null) {
|
|
discoveryRef.current = createBluetoothDiscovery({
|
|
onSearchingChange: (searching) => setState((s) => ({ ...s, searching })),
|
|
onConnectedChange: (connected) => setState((s) => ({ ...s, connected })),
|
|
onDeviceListChange: (deviceList) => setState((s) => ({ ...s, deviceList })),
|
|
onProfileChange: (profileId) => setState((s) => ({ ...s, profileId })),
|
|
onStageChange: (stage) => setState((s) => ({ ...s, stage })),
|
|
onDeviceInfoChange: (deviceInfo) => setState((s) => ({ ...s, deviceInfo })),
|
|
onAuthChange: (authResult) => setState((s) => ({ ...s, authResult }))
|
|
// onError / onRadarData / onProtocolState / onProtocolMsg 按页面需要接入
|
|
})
|
|
}
|
|
|
|
// 卸载时统一清理(停扫描、断连接、解绑事件)
|
|
useEffect(() => {
|
|
const discovery = discoveryRef.current
|
|
return () => {
|
|
discovery?.cleanupDiscovery()
|
|
discoveryRef.current = null
|
|
}
|
|
}, [])
|
|
|
|
const discovery = discoveryRef.current!
|
|
|
|
return { state, discovery }
|
|
}
|