RxJS внутри функционального компонента React

Основная часть этой проблемы заключается в том, что я пытаюсь создать реагирующие компоненты, каждый из которых имеет наблюдаемую на основе свойств компонента. У меня есть несколько мест, каждое с показателями окружающей среды, в которых я хотел бы независимо создавать графики D3 для использования RxJS для управления поступающими данными.

У меня есть рабочая версия этой концепции:

https://www.lloydrichardsdesign.com/experiment/021

Проблема, с которой я столкнулся в этом примере, заключается в том, что я жестко запрограммировал хранилище огня, наблюдаемое вне компонента. Но в моей следующей версии я хотел бы создать компонент, который может передавать locationId наблюдаемому объекту при его загрузке, а затем каждый компонент будет управлять своим собственным состоянием.

Это выглядит так:

import { Observable } from "rxjs"
import { Dispatch, SetStateAction, useEffect, useState } from "react";

const useObservable = (observable: Observable<any>, setter: Dispatch<SetStateAction<any>>) => {
 useEffect(()=>{
let subscription = observable.subscribe(result => {
    setter(result);
});
return ()=> subscription.unsubscribe()
 },[observable, setter])
}


const LocationItem: React.FC<LocationProps> = ({ location }) => {
   const [readings, setReadings] = useState<Array<Reading>>([]);
   const dataObservable = collectionData(
      db
      .collection('mimirReading')
      .where('locationId', '==', location.id)
      .orderBy('timestamp', 'desc')
      .limit(48)
  );

  useObservable(dataObservable, setReadings);
   return(
    <ol>
     {readings.map(r=><li>{r.timestamp}</li>)}
    </ol>
   )
}

Проблема в том, что это приводит к тому, что useObservable вызывается снова и снова, никогда не возвращая никаких данных. Я получаю пустое состояние чтения, и моя консоль сходит с ума.

Я подумал, что мне нужно было создать dataObservable при первом монтировании компонента, поэтому в useEffect, но тогда я получу ошибки, связанные с вызовом useEffect внутри себя. И, наконец, я попытался вытащить подписку из useEffect при первом создании компонента, но затем наблюдаемый объект никогда не собирал никакой информации.

Вот так:

useEffect(() => {
    const dataObservable = collectionData(
      db
        .collection('mimirReading')
        .where('locationId', '==', location.id)
        .orderBy('timestamp', 'desc')
        .limit(48)
    ).subscribe((reads) => {
      console.log(reads);
      setReadings(reads as Array<Reading>);
    });
    console.log(dataObservable);
    return () => dataObservable.unsubscribe();
  }, []);

Я немного растерялся и не знаю, что делать. Если у кого-то есть идеи или решения, мы будем очень признательны!

1 ответ

Решение

Держать useObservable изолирован, и создайте наблюдаемое значение (запомненное для идентификатора местоположения), чтобы передать ему:

const useObservable = (observable, setter) => {
  useEffect(() => {
    let subscription = observable.subscribe(result => {
      setter(result);
    });
    return () => subscription.unsubscribe()
    },
    [observable, setter]
  );
};

const LocationItem = ({ location }) => {
  const [readings, setReadings] = useState([]);

  const dataObservable = useMemo(() => {
    return collectionData(
      db
        .collection('mimirReading')
        .where('locationId', '==', location.id)
        .orderBy('timestamp', 'desc')
        .limit(48)
    );
  }, [location.id]);

  useObservable(dataObservable, setReadings);

  return (
    <ol>
      {readings.map((r) => (
        <li>{r.timestamp}</li>
      ))}
    </ol>
  );
};

При желании я бы также предложил изменить право собственности государства на useObservable:

const useObservable = (observable) => {
  const [value, setValue] = useState();
  useEffect(() => {
    let subscription = observable.subscribe((result) => {
      setValue(result);
    });
    return () => subscription.unsubscribe();
  }, [observable]);
  return value;
};

Таким образом, вам не нужен установщик внешнего состояния, он всегда обрабатывается внутри хука. Вы также можете использовать setState в пределах useObservable чтобы зафиксировать ошибку наблюдаемого и завершить события.

Другие вопросы по тегам