我是React和Recoil的新手,希望通过使用Web Bluetooth API实时收集的数据显示实时图表(使用D3(。
简而言之,在调用await myCharacteristic.startNotifications()
和myCharacteristic.addEventListener('characteristicvaluechanged', handleNotifications)
之后,每次从蓝牙设备通知新值时,都会调用handleNotifications
回调(参见本示例(。
我正在使用钩子,并试图从回调中修改反冲状态(这被简化到了极致,我希望它具有代表性(:
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>
}
如果我想在应用程序的某个地方显示最新的值,这很好。然而,我感兴趣的是将最后几个(比方说10个(值保留在一个循环缓冲区中,以绘制D3图表。
我尝试了一些类似的东西
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>
}
然而,很明显,我遇到了这里描述的问题,其中函数使用的是在渲染过程中捕获的旧版本的temperatureList
。结果,temperatureState
总是空的,然后用一个元素的列表替换。
如何在从外部回调更新的React状态/Recoil原子中维护一致的列表?我认为这个问题有点类似,但我想避免使用另一个扩展,如雷科Nexus。
useSetRecoilState
接受一个updater函数作为参数,将要更新的值作为第一个参数:
export function BluetoothControls() {
const setTemperatureList = useSetRecoilState(temperatureListState);
const notify = async () => {
...
temperatureCharacteristic.addEventListener('characteristicvaluechanged', event => {
setTemperatureList(t => {
let temperatureListCopy = t.map(x => x);
temperatureListCopy.push(event.target.value.getInt16(0))
if (temperatureListCopy.length > 10)
temperatureListCopy.shift()
return temperatureListCopy
})
}
}
return <button onClick={nofity}/>Start notifications</button>
}
这解决了问题,因为updater函数只在事件上求值。