When using "const [devices, setDevices] = useState([])" or any statefunction inside the scaning from 'startDeviceScan' the event stops
The event stop and no more devices are found
[CoreBluetooth] API MISUSE:
2020-01-19 20:06:06.293648+0100 Zipforce[23222:18432691] [CoreBluetooth] XPC connection invalid
const bleManager = new BleManager();
const [deviceScan, setDeviceScan] = useState(false)
const [devices, setDevices] = useState([])
const stopDeviceScan = () => {
console.trace('stopDeviceScan')
bleManager.stopDeviceScan()
setDeviceScan(false)
}
const addDevice = (device) => {
console.trace('addDevice')
console.log(device.id, device.name, device.localName)
if (device.isConnectable &&
device.localName &&
devices.findIndex((x) => x.id == device.id) === -1) {
setDevices([...devices, {
id: device.id,
name: device.name,
localName: device.localName,
isConnectable: device.isConnectable,
rssi: device.rssi
}])
}
}
const startDeviceScan = () => {
console.trace('startDeviceScan')
bleManager.startDeviceScan(null, { allowDuplicates: false }, (error, device) => {
if (error) {
console.error(error)
} else {
addDevice(device)
}
})
}
useEffect(() => {
console.trace('Init - Timer')
let timerId = setTimeout(() => {
stopDeviceScan()
}, 15000)
return () => clearTimeout(timerId);
}, [])
useEffect(() => {
console.trace('Init')
setDevices([])
setDeviceScan(true)
const subscription = bleManager.onStateChange((state) => {
switch (state) {
case 'PoweredOn':
console.trace('PoweredOn')
subscription.remove();
startDeviceScan()
break;
default:
break;
}
})
}, [])
useEffect(() =>{
console.log('useEffect',devices)
},[devices])
I've noticed the same thing when using monitorCharacteristicForService(). If I try to update state inside of my listener function, it only runs once and then stops.
Can you send me more logs from the native side?
Also experiencing this, has anyone found a workaround?
For us the stream of characteristics from Characteristic.monitor is interrupted when useState is called
We found that this is because the device is being disconnected on the rerender but not got a solution yet
(By the way the library is great)
I am expierencing the same thing! Whenever i use setState, i need to reconnect...
The issue does not occur if you move the search function outside of the main function
@senner007 Thanks for the quick reply! But how exactly do you mean that? Outside of the Component?
@Krister-Johansson I think your expected behavior description is incorrect.
@senner007 Thanks for the quick reply! But how exactly do you mean that? Outside of the Component?
Extract the search function and pass in a function to update the state
@senner007
But isnt this exactly whats happening here:
const startDeviceScan = () => {
console.trace('startDeviceScan')
bleManager.startDeviceScan(null, { allowDuplicates: false }, (error, device) => {
if (error) {
console.error(error)
} else {
addDevice(device)
}
})
}
Because you scan, and call another function with the device to save it to the state
Yes, it is the same and it also works for me. I can't reproduce the issue then....
This seems to be the same issue here i am facing:
I dont see that the scanning stops, but I do see that this:
setDevices([...devices, {
id: device.id,
name: device.name,
localName: device.localName,
isConnectable: device.isConnectable,
rssi: device.rssi
}])
needs to be this :
setDevices(prevDevices => [...prevDevices, {
id: device.id,
name: device.name,
localName: device.localName,
isConnectable: device.isConnectable,
rssi: device.rssi
}])
hmmm i tried this.. but this does not resolve the issue for me... really weird that i have this problem only with useState... if i use a Class-Component and not Functional-Component with setState i dont have this issue at all!
I keep getting the error "Device is not connected" with the right DeviceID
Just to make things clear. You shouldn't create BleManager inside the component. Is that true in your case?
@Cierpliwy Wow! Just tried it! Fixed my problem! Damn... that was really stupid of me!
I ve been trying to fix this for at least 2 Days now...
But how come this works inside a Class-Component:
import React, { Component } from 'react';
import { Platform, View, Text } from 'react-native';
import { BleManager } from 'react-native-ble-plx';
export default class SensorsComponent extends Component {
constructor() {
super()
this.manager = new BleManager()
this.state = {info: "", values: {}}
this.prefixUUID = "f000aa"
this.suffixUUID = "-0451-4000-b000-000000000000"
this.sensors = {
0: "Temperature",
1: "Accelerometer",
2: "Humidity",
3: "Magnetometer",
4: "Barometer",
5: "Gyroscope"
}
}
.....
Taken from https://www.polidea.com/blog/ReactNative_and_Bluetooth_to_An_Other_level/
There are problems with how hooks are used in the initial code. This has to do with closures.
devices array will always be empty in addDevice()
Yeah, example was prepared to be concise. In general I highly recommend to create clear separation between BLE logic and UI of your application.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
@Cierpliwy Could you explain why the BleManager cannot be instantiated inside the component?
Also, it would be extremely helpful to have an example react-native-ble-plx implementation using functional components. Do you know if this exists?
It can be instantiated using functional approach. It's just that the first example has errors. See what I wrote about the closure issue
i believe the problem arrises from the state hook causing a re-render.
i fixed it by useRef for the manager and useReducer, rather then useState for storing the devices i get back from the scan.
i believe the problem arrises from the state hook causing a re-render.
i fixed it by useRef for the manager and useReducer, rather then useState for storing the devices i get back from the scan.
Could you please post your code?
i believe the problem arrises from the state hook causing a re-render.
i fixed it by useRef for the manager and useReducer, rather then useState for storing the devices i get back from the scan.Could you please post your code?
i've moved all of this into a context manager but i can post a few snippets diggin into the IED history ;)
they probably won't work as is, but it'll get you going in the right direction
setup a reducer and actions to dispatch:
const initialState = [];
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return [
...state,
{
id: action.device.id,
name: action.device.name,
device: action.device
}];
case 'set':
return action.devices;
case 'clear':
return initialState;
default:
return state;
}
}
const clear = () => ({type: 'clear'});
const addDevice = (device) => ({
type: 'add',
device
});
setup the useReducer and useRef hooks:
const [devices, dispatch] = useReducer(reducer, initialState);
const manager = useRef(new BleManager());
intialize the bleManager and start a scan:
const scan = () => {
manager.current.startDeviceScan(null, null, (error, device) => {
if (error) {
console.log(error);
return;
}
console.log(device.name);
dispatch(addBLE(device));
});
};
const startScan = () => {
dispatch(clear());
const subscription = manager.current.onStateChange((state) => {
if (state === 'PoweredOn') {
scan();
}
}, true);
};
call startScan with a useEffect or a button, that'll start the scan, to see the data, loop through devices
{devices.map((device, index) => {
console.log(device.id)
})}
hope this helps
You need to place your manager = new Manager() code outside of the function or class. Otherwise react renders a new manager on every state change.
Most helpful comment
I've noticed the same thing when using monitorCharacteristicForService(). If I try to update state inside of my listener function, it only runs once and then stops.