Как использовать обновления Firestore в реальном времени (onSnapshot) с помощью redux-observable / rxjs?

Я могу настроить redux-observable с помощью обычных запросов Firestore

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

Но я хочу преобразовать это в режим реального времени, я попытался изменить

return query.get().then(snapshot => {

to

return query.onSnapshot(snapshot => {

Но не получается ... Полагаю, это не обещание? Как мне решить эту проблему?


person Jiew Meng    schedule 02.06.2018    source источник


Ответы (1)


Вы правы, метод onSnapshot не возвращает обещание. Вместо этого он возвращает функцию, которую можно использовать для отмены подписки на уведомления об изменениях. До тех пор, пока не будет вызвана эта функция отмены подписки, обратный вызов, переданный методу onSnapshot, будет вызываться каждый раз при изменении документа. (документация указывает, что обратный вызов также будет вызываться немедленно с текущим содержание документа.)

Такие функции, как onSnapshot, которые используют функцию обратного вызова несколько раз, могут быть «преобразованы» в наблюдаемое с помощью функции fromEventPattern. fromEventPattern принимает в качестве параметров две функции. Первая функция, которую вы передаете, должна вызывать 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),
      )),
    )
  }),
)

Обратите внимание, что я ввел takeUntil в поток моментальных снимков. Без него (или чего-то подобного) поток снимков никогда не закончится. Другое возможное изменение - использование switchMap вместо mergeMap. Как отказаться от подписки, зависит только от вашего варианта использования.

person seniorquico    schedule 08.06.2019