React-Native调用startDeviceScan报空值TypeError错误求助
错误原因及修复方案
核心错误原因
TypeError: Cannot read property 'startDeviceScan' of null 是因为你在**bleManager状态为null时调用了它的方法**,主要出现在两个场景:
- 首次进入应用时权限未通过,
bleManager未初始化,后续点击「Start Scanning」按钮直接调用bleManager.startDeviceScan - 组件初始化逻辑存在异步状态更新的疏漏,导致调用方法时实例还未创建
具体修复步骤
1. 所有调用bleManager方法前先做非空检查
不管是启动扫描还是停止扫描,都要先判断bleManager是否存在,避免空指针错误:
修改「Start Scanning」按钮逻辑
<Button title="Start Scanning" onPress={() => { requestBluetoothPermissions((isGranted) => { if (isGranted) { setDevices([]); // 不存在则创建新实例 let currentManager = bleManager; if (!currentManager) { currentManager = new BleManager(); setBleManager(currentManager); } currentManager.startDeviceScan( null, // 传null扫描所有设备,可替换为你的服务UUID数组过滤 { scanMode: BleManager.ScanMode.LowLatency }, // 修正ScanMode的引用方式 (error, device) => { if (error) { console.log(error.message); return; } if (device.name) { // 避免重复添加设备 setDevices(prev => { if (!prev.some(d => d.id === device.id)) { return [...prev, device]; } return prev; }); } } ); setScanning(true); } }); }} />
修改「Stop Scanning」按钮逻辑
<Button title="Stop Scanning" onPress={() => { if (bleManager) { // 先检查实例是否存在 bleManager.stopDeviceScan(); } setScanning(false); }} />
2. 修复未定义变量问题
代码中serialUUIDs和ScanMode未定义,需修正:
serialUUIDs.serviceUUID替换为你的实际蓝牙服务UUID,或传null扫描所有设备ScanMode需从BleManager类中引用,即BleManager.ScanMode.LowLatency
3. 优化组件初始化与销毁逻辑
原useEffect空依赖导致只执行一次,且未处理权限未通过的情况,同时组件卸载时要清理BLE资源:
useEffect(() => { // 挂载时先请求权限,初始化BLE实例 requestBluetoothPermissions((isGranted) => { if (isGranted && !bleManager) { const manager = new BleManager(); setBleManager(manager); } }); // 卸载时停止扫描并销毁实例 return () => { if (bleManager) { bleManager.stopDeviceScan(); bleManager.destroy(); } }; }, [bleManager]);
4. 修正权限判断逻辑
react-native-permissions返回的权限结果是字符串('granted'/'denied'),而非PermissionsAndroid.RESULTS枚举值,需修正判断:
function requestBluetoothPermissions(cb) { if (Platform.OS === 'android') { DeviceInfo.getApiLevel().then((apiLevel) => { if (apiLevel < 31) { PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { title: '位置权限请求', message: '蓝牙低功耗需要位置权限才能扫描设备', buttonNeutral: '稍后询问', buttonNegative: '取消', buttonPositive: '确定', }, ).then((granted) => { cb(granted === PermissionsAndroid.RESULTS.GRANTED); }); } else { requestMultiple([ PERMISSIONS.ANDROID.BLUETOOTH_SCAN, PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION, ]).then((result) => { const isGranted = result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] === 'granted' && result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] === 'granted' && result[PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION] === 'granted'; cb(isGranted); }); } }); } else { cb(true); } }
完整修复代码
import React, { useState, useEffect } from 'react'; import { SafeAreaView, View, Text, Button, FlatList, Platform, PermissionsAndroid, } from 'react-native'; import BleManager from 'react-native-ble-plx'; import { PERMISSIONS, requestMultiple } from 'react-native-permissions'; import DeviceInfo from 'react-native-device-info'; const App = () => { const [devices, setDevices] = useState([]); const [scanning, setScanning] = useState(false); const [bleManager, setBleManager] = useState(null); function requestBluetoothPermissions(cb) { if (Platform.OS === 'android') { DeviceInfo.getApiLevel().then((apiLevel) => { if (apiLevel < 31) { PermissionsAndroid.request( PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION, { title: '位置权限请求', message: '蓝牙低功耗需要位置权限才能扫描设备', buttonNeutral: '稍后询问', buttonNegative: '取消', buttonPositive: '确定', }, ).then((granted) => { cb(granted === PermissionsAndroid.RESULTS.GRANTED); }); } else { requestMultiple([ PERMISSIONS.ANDROID.BLUETOOTH_SCAN, PERMISSIONS.ANDROID.BLUETOOTH_CONNECT, PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION, ]).then((result) => { const isGranted = result[PERMISSIONS.ANDROID.BLUETOOTH_CONNECT] === 'granted' && result[PERMISSIONS.ANDROID.BLUETOOTH_SCAN] === 'granted' && result[PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION] === 'granted'; cb(isGranted); }); } }); } else { cb(true); } } useEffect(() => { requestBluetoothPermissions((isGranted) => { if (isGranted && !bleManager) { const manager = new BleManager(); setBleManager(manager); } }); return () => { if (bleManager) { bleManager.stopDeviceScan(); bleManager.destroy(); } }; }, [bleManager]); const startScanning = () => { requestBluetoothPermissions((isGranted) => { if (isGranted) { setDevices([]); let currentManager = bleManager; if (!currentManager) { currentManager = new BleManager(); setBleManager(currentManager); } currentManager.startDeviceScan( null, { scanMode: BleManager.ScanMode.LowLatency }, (error, device) => { if (error) { console.log(error.message); return; } if (device.name) { setDevices((prev) => { if (!prev.some((d) => d.id === device.id)) { return [...prev, device]; } return prev; }); } } ); setScanning(true); } }); }; const stopScanning = () => { if (bleManager) { bleManager.stopDeviceScan(); } setScanning(false); }; return ( <SafeAreaView style={{ flex: 1, padding: 16 }}> <View style={{ marginBottom: 24 }}> {scanning ? ( <Button title="Stop Scanning" onPress={stopScanning} /> ) : ( <Button title="Start Scanning" onPress={startScanning} /> )} </View> <FlatList data={devices} renderItem={({ item }) => ( <Text style={{ padding: 8, borderBottomWidth: 1, borderBottomColor: '#eee' }}> {item.name} ({item.id}) </Text> )} keyExtractor={(item) => item.id} /> </SafeAreaView> ); }; export default App;
内容的提问来源于stack exchange,提问作者profundusman
相关产品推荐
相关产品推荐

