Skip to content

Instantly share code, notes, and snippets.

@goatslacker
Last active November 5, 2015 20:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save goatslacker/900623f50310406d7c57 to your computer and use it in GitHub Desktop.
Save goatslacker/900623f50310406d7c57 to your computer and use it in GitHub Desktop.
dataSources.js
import isPromise from 'is-promise'
import { generateActions } from 'create-actions'
// get the action creators that will be created given a namespace
export const getActionCreators = (namespace) => {
return generateActions(namespace, [
'begin',
'success',
'failure',
'end',
])
}
// convenience function so you can use function call form instead of creating
// the object yourself
export const send = (id, value) => {
return { id, value }
}
// Creates a function that when called with a dispatcher will
// create all the necessary actions for you and dispatch them in the correct
// order. Basic caching is also provided by the utility
const createAsyncDispatch = (namespace, fn, opts) => {
const { begin, success, failure, end } = {
...getActionCreators(namespace),
...opts,
}
const store = { pending: {}, requests: {} }
return (...args) => dispatcher => {
const {
id = namespace,
value,
} = fn(...args)
const beginDispatch = () => dispatcher.dispatch(begin(id))
const endDispatch = () => {
delete store.pending[id]
if (!opts.cache) delete store.requests[id]
dispatcher.dispatch(end(id))
}
const asyncDispatch = (value) => {
value.then(beginDispatch, beginDispatch)
const result = value.then((res) => {
dispatcher.dispatch(success(res))
return res
}, (err) => {
dispatcher.dispatch({
...failure(err),
error: true,
})
return Promise.reject(err)
})
store.pending[id] = true
store.requests[id] = result
value.then(endDispatch, endDispatch)
return result
}
const result = value({
state: opts.getState ? opts.getState(dispatcher) : null,
isLoading: () => store.pending.hasOwnProperty(id),
getRequest: () => store.requests[id],
}, dispatcher)
// remote request
if (isPromise(result)) {
return asyncDispatch(result)
// a function, handle your own async dispatches
} else if (typeof result === 'function') {
return result(asyncDispatch)
}
// local value
return result
}
}
export default createAsyncDispatch
@goatslacker
Copy link
Author

Usage

const fetchUser = createAsyncDispatch('user', (userId) => {
  return {
    id: `user/${userId}`,
    value: (req) => {
      // do your own checks here if its in cache or if we're loading or whatever...
      if (req.isLoading) return
      if (UserStore.getUser()) return UserStore.getUser()

      return xhr(`/users/${userId}`).then((res) => res.data)
    }
  }
})

fetchUser(12).then((user) => {
  // do something with user...
})
// automatic 'user/success' action is called on success. Same with failure, begin, and end.


// listening to those actions...
// lets get the action creators. `user` is the same "namespace" you used for createAsyncDispatch
const actions = getActionCreators('user')

// setup your store
class UserStore {
  constructor() {
    this.bindListeners({ userReceived: actions.success })
  }

  userReceived() {
    // do something
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment