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
чтобы зафиксировать ошибку наблюдаемого и завершить события.