0

有点小众的问题,但我知道问题是什么,所以希望这里有人可以帮助我。这是 Observable/RXFire 问题,而不是 xstate 问题。

我有这台机器调用一个可观察的:

export const tribeMachine = Machine(
  {
    id: "council",
    initial: "init",
    context: {},
    states: {
      init: {
        invoke: {
          id: "gettribes",
          src: () =>
            collectionData(database.collection("tribes")).pipe(
              concatAll(),
              map(x => ({ type: "STORE", x }))
            ),
          onDone: "loaded"
        },
        on: {
          STORE: {
            actions: "storetribes"
          },
          CANCEL: "loaded"
        }
      },
      loaded: {
        entry: () => console.log("loaded")
      },
      error: {
        entry: () => console.log("error")
      }
    }
  },
  {
    actions: {
      storetribes: (context, event) => console.log("hello")
    }
  }
);

它应该工作的方式是机器在加载时调用 observable,然后一旦 obs 完成发出其值并调用 complete(),invoke.onDone 被调用并且机器转换到“加载”状态。

当我使用通过 complete() 调用创建的普通 observable 时,或者当我将 take(#) 添加到 .pipe() 的末尾时,转换工作。

但由于某种原因,来自 RXFire 的 collectionData() 的可观察数据没有发出“完整”信号……机器就坐在那里。

我尝试在末尾添加一个 empty() 并 concat() 对 observables 添加一个完整的信号到管道的末尾......但后来我发现 empty() 已被弃用并且它没有似乎无论如何都可以工作。

有一段时间我的头撞在墙上。任何帮助表示赞赏。


编辑:

解决方案:

我误解了 collectionData() 的目的。它是一个监听器,所以它不应该完成。我在圆孔里放了一个方钉。解决方案是重构 xstate 机器,所以我根本不需要调用 onDone。

尽管如此,还是感谢您的回答。


EDIT2:让它工作。

take(1) 可以在 concatAll() 之前调用。我想如果你先调用它,它会结束流,但事实并非如此。管道中的其他运算符仍然适用。所以我采取(1) 来获取单个数组,使用 concatAll() 将数组展平为单个对象的流,然后将该数据映射到触发 STORE 操作的新对象。然后 store 操作将数据设置为机器的上下文。

export const tribeMachine = Machine({
    id: 'council',
    initial: 'init',
    context: {
        tribes: {},
        markers: []
    },
    states: {
        init: {
            invoke: {
                id: 'gettribes',
                src: () => collectionData(database.collection('tribes')).pipe(
                    take(1),
                    concatAll(),
                    map(value => ({ type: 'TRIBESTORE', value })),
                ),
                onDone: 'loaded'
            },
            on: {
                TRIBESTORE: {
                    actions: ['storetribes', 'logtribes']
                },
                CANCEL: 'loaded'
            }
        },
        loaded: {
        },
        error: {
        }
    }
},
    {
        actions: {
            storetribes: assign((context, event) => {
                return {
                    tribes: {
                        ...context.tribes,
                        [event.value.id]: event.value
                     },
                     markers: [
                         ...context.markers,
                         {
                             lat: event.value.lat,
                             lng: event.value.lng,
                             title: event.value.tribeName
                         }
                        ]
                     }
            })
        }
    }
)

感谢大家的帮助!

4

1 回答 1

1

Observables 可以随着时间的推移返回多个值,因此由collectionData()决定何时完成(即导致调用 complete() )。

但是,如果您只想从 observable 中获取 1 个值,您可以尝试:

  collectionData(database.collection("tribes")).pipe(
              take(1),
              concatAll(),
              map(x => ({ type: "STORE", x }))
            ),

一旦您从 中获取 1 个值,这将导致可观察对象完成collectionData()

注意:这可能不是最好的解决方案,因为它取决于您使用的可观察流的工作方式。我只是强调你可以使用take(1)1 值并完成源 observable。

于 2020-03-29T06:34:23.730 回答