如何在 redux-observable/rxjs 中使用 Firestore 实时更新(onSnapshot)?

Jie*_*eng 5 firebase reactjs redux redux-observable google-cloud-firestore

我可以使用普通的 Firestore 查询设置 redux-observable

export const getStatementsEpic = (action$, store) => {
  return action$.ofType(GET_STATEMENTS)
    .filter(() => {
      const state = store.getState()
      return state.auth.user
    })
    .mergeMap(() => {
      console.log('action', store.getState().auth.user.uid)
      const db = firebase.firestore()
      db.settings({ timestampsInSnapshots: true })
      const query = db.collection('users')
        .doc(store.getState().auth.user.uid)
        .collection('statements')
        .orderBy('uploadedOn', 'desc')
        .limit(50)
      return query.get().then(snapshot => {
        console.log('Should have gotten snapshot')
        return getStatementsSnapshot(snapshot)
      })
    })
}
Run Code Online (Sandbox Code Playgroud)

但我想将其转换为实时,我尝试更改

return query.get().then(snapshot => {
Run Code Online (Sandbox Code Playgroud)

return query.onSnapshot(snapshot => {
Run Code Online (Sandbox Code Playgroud)

但它不起作用......我想这不是一个承诺?我该如何解决?

sen*_*ico 5

你是对的,该onSnapshot方法不返回承诺。相反,它返回一个可用于取消订阅更改通知的函数。在调用取消订阅函数之前,onSnapshot每次文档更改时都会调用传递给该方法的回调。(文档表明回调也将立即使用当前文档内容调用。)

类似多次使用回调函数的函数onSnapshot可以使用该函数“转换”为可观察对象fromEventPatternfromEventPattern将两个函数作为参数。您传递的第一个函数需要 call onSnapshot,将 RxJS 定义的处理程序作为回调传递给它。您传递的第二个函数需要调用 返回的取消订阅函数onSnapshot。当您订阅可观察对象时(即在您的史诗中使用它),RxJS 将调用第一个函数。当您取消订阅可观察对象时,RxJS 将调用第二个函数。

fromEventPattern以下是更新后使用的代码和新 RxJS 管道的示例:

export const getStatementsEpic = (action$, state$) => action$.pipe(
  ofType(GET_STATEMENTS),
  withLatestFrom(state$),
  filter(([action, state]) => state.auth.user),
  mergeMap(([action, state]) => {
    const db = firebase.firestore()
    db.settings({ timestampsInSnapshots: true })
    return fromEventPattern(
      handler => db.collection('users')
        .doc(state.auth.user.uid)
        .collection('statements')
        .orderBy('uploadedOn', 'desc')
        .limit(50)
        .onSnapshot(handler),
      (handler, unsubscribe) => unsubscribe(),
    ).pipe(
      map(getStatementsSnapshot),
      takeUntil(action$.pipe(
        ofType(STOP_GET_STATEMENTS),
      )),
    )
  }),
)
Run Code Online (Sandbox Code Playgroud)

请注意,我已经介绍了takeUntil快照流。没有它(或类似的东西),快照流将永远不会结束。另一个可能的更改是使用switchMap而不是mergeMap. 如何取消订阅仅取决于您的用例。