Home Reference Source Repository

src/async-tracker.js

 import { createAction } from 'redux-actions'

const loadStart = createAction('@reduxAsyncConnect/BEGIN_GLOBAL_LOAD')

const loadEnd = createAction('@reduxAsyncConnect/END_GLOBAL_LOAD')

export default function asyncMiddleware({ dispatch }) {  

  const tracker = new Tracker(dispatch)

  return next => action => {

    if (action.error) {
      console.error(`Error: ${action.type}\n`, action.payload)
      return next(action)
    }

    if (!__CLIENT__) {
      return next(action)
    }

    if (isPromise(action.payload)) {
      dispatch(loadStart())
      tracker.enqueue(action.payload)
    }

    return next(action)
  }
}

class Tracker {
  
  promises = []

  get loading() {
    return !!this.promises.length
  }

  constructor(dispatch) {
    this.dispatch = dispatch
  }
  
  enqueue(promise) {
    this.promises.push(promise)
    promise.then(x => {
      this.dequeue(promise)
      return x
    })
    .catch(err => {
      this.dequeue(promise)
      return Promise.reject(err)
    })
  }
  
  dequeue(promise) {
    let index = this.promises.indexOf(promise)
    this.promises.splice(index, 1)
    if (!this.loading) {
      setTimeout(x => {
        this.dispatch(loadEnd())
      }, 0)
    }
  }
}

function isPromise(x) {
  return x && x.then
}