You're correct, the onSnapshot method does not return a promise. Instead, it returns a function that can be used to unsubscribe from change notifications. Until that unsubscribe function is invoked, the callback passed to the onSnapshot method will be called every time the document changes. (The documentation indicates the callback will also be called immediately with the current document contents.)

Functions like onSnapshot that use a callback function multiple times can be "converted" into an observable using the fromEventPattern function. fromEventPattern takes two functions as parameters. The first function you pass needs to call onSnapshot, passing it a RxJS-defined handler as the callback. The second function you pass needs to call the unsubscribe function returned by onSnapshot. RxJS will call the first function when you subscribe to the observable (i.e. use it in your epic). RxJS will call the second function when you unsubscribe from the observable.

Here's an example of your code updated to use fromEventPattern and the new RxJS pipes:

export const getStatementsEpic = (action$, state$) => action$.pipe(
  filter(([action, state]) => state.auth.user),
  mergeMap(([action, state]) => {
    const db = firebase.firestore()
    db.settings({ timestampsInSnapshots: true })
    return fromEventPattern(
      handler => db.collection('users')
        .orderBy('uploadedOn', 'desc')
      (handler, unsubscribe) => unsubscribe(),

Take note that I've introduced takeUntil to the snapshot stream. Without it (or something like it), the snapshot stream will never end. Another possible change is using switchMap instead of mergeMap. How to unsubscribe just depends on your use case.

Related Query

More Query from same tag