0

我是使用 React 和 Recoil 的新手,我想从使用Web Bluetooth API实时收集的数据中显示实时图表(使用 D3)。

简而言之,在调用await myCharacteristic.startNotifications()and之后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 atom 中维护一致的列表?我认为这个问题有点相似,但我想避免使用像 Recoil Nexus 这样的另一个扩展。

4

1 回答 1

0

useSetRecoilState接受一个更新函数作为参数,要更新的值作为第一个参数:

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>
}

这解决了这个问题,因为更新函数仅在事件上进行评估。

于 2021-03-23T01:54:17.910 回答