I am new to using React and Recoil and want to display real-time charts (using D3) from data that is gathered in real-time using the Web Bluetooth API.
In a nutshell, after calling await myCharacteristic.startNotifications()
and myCharacteristic.addEventListener('characteristicvaluechanged', handleNotifications)
, the handleNotifications
callback is called each time a new value is notified from a Bluetooth device (see this example).
I am using hooks and tried to modify a recoil state from the callback (this was simplified to the extreme, I hope it is representative):
export const temperatureState = atom({
key: 'temperature',
default: 0
})
export function BluetoothControls() {
const setTemperature = useSetRecoilState(temperatureState);
const notify = async () => {
...
temperatureCharacteristic.addEventListener('characteristicvaluechanged', event => {
setTemperature(event.target.value.getInt16(0))
}
}
return <button onClick={nofity}/>Start notifications</button>
}
This work fine if I want to display the latest value somewhere in the app. However, I am interested in keeping the last few (let's say 10) values in a circular buffer to draw a D3 chart.
I tried something along the lines of:
export const temperatureListState = atom({
key: 'temperature-list',
default: []
})
export function BluetoothControls() {
const [temperatureList, setTemperatureList] = useRecoilState(temperatureListState);
const notify = async () => {
...
temperatureCharacteristic.addEventListener('characteristicvaluechanged', event => {
let temperatureListCopy = temperatureList.map(x => x);
temperatureListCopy.push(event.target.value.getInt16(0))
if (temperatureListCopy.length > 10)
temperatureListCopy.shift()
setTemperatureList(temperatureListCopy)
}
}
return <button onClick={nofity}/>Start notifications</button>
}
However, is is pretty clear that I am running into the issue described here where the function is using an old version of temperatureList
that is captured during render. As a result, temperatureState
is always empty and then replaced with a list of one element.
How to maintain a consistent list in a React state/Recoil atom that is updated from an external callback? I think this issue is a bit similar but I'd like to avoid using another extension like Recoil Nexus.