简体   繁体   English

React 函数式组件中的 RxJS

[英]RxJS inside React Functional Component

The bulk of this problem is that I'm trying to create react components that each have an observable based on the component's props.这个问题的大部分是我试图创建反应组件,每个组件都有一个基于组件道具的可观察对象。 I have several locations, each with environmental readings, that I would like to independently generate D3 graphs for using RxJS to manage data coming in.我有几个位置,每个位置都有环境读数,我想独立生成 D3 图以使用 RxJS 来管理传入的数据。

I have a working version of this concept here:我在这里有一个这个概念的工作版本:

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

The problem I'm having is with this example, I've hardcoded the firestore observable outside of the component.我遇到的问题是在这个例子中,我在组件外部对 firestore observable 进行了硬编码。 But in my next version, I would like to make a component that can feed in the locationId to the observable when it loads and then each component manage their own state.但是在我的下一个版本中,我想制作一个组件,该组件可以在加载时将 locationId 提供给 observable,然后每个组件管理自己的状态。

This looks like:这看起来像:

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

The problem is, this results in the useObservable being called over and over and over again never giving back any data.问题是,这导致 useObservable 被一遍又一遍地调用,从不返回任何数据。 I end up with an empty readings state and my console going crazy.我最终得到一个空的读数状态,我的控制台发疯了。

I figured, I had to create the dataObservable when the component first mounts, so in useEffect, but then I would get errors related to calling useEffect inside itself.我想,我必须在组件第一次挂载时创建 dataObservable,所以在 useEffect 中,但是我会得到与在其内部调用 useEffect 相关的错误。 And lastly I tried pulling the subscription out and into the useEffect when first creating the component, but then the observable never collected any information.最后,我尝试在第一次创建组件时将订阅拉出并放入 useEffect,但随后可观察对象从未收集任何信息。

Like so:像这样:

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();
  }, []);

I'm at a bit of a loss now and don't know what to do.我现在有点不知所措,不知道该怎么办。 If anyone has any ideas or solutions it would be very much appreciated!如果有人有任何想法或解决方案,将不胜感激!

Keep the useObservable hook isolated, and create a observable value (memoized to the location id) to pass to it:保持useObservable钩子隔离,并创建一个可观察值(记忆到位置 id)以传递给它:

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

Optionally, I'd further suggest changing the ownership of the state to useObservable :或者,我还建议将状态的所有权更改为useObservable

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

This way you don't need the outer state setter, it's always handled within the hook.这样你就不需要外部状态设置器,它总是在钩子内处理。 You could also use setState within useObservable to capture the observable's error and complete events.您还可以在useObservable使用setState来捕获 observable 的错误和完成事件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM