Skip to content

Instantly share code, notes, and snippets.

@sfrdmn
Created December 1, 2017 16:02
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 sfrdmn/7f38b6d264b9af79259f0da2784c99bd to your computer and use it in GitHub Desktop.
Save sfrdmn/7f38b6d264b9af79259f0da2784c99bd to your computer and use it in GitHub Desktop.
mcr_post_13
const { createSelector, createStructuredSelector } = require('reselect')
const identity = x => x
const createAsyncSelector = (
asyncSelectors = {},
syncSelectors = {},
compute = identity
) => {
// Let’s do a little argument dance to optionally support the uniform
// handling of synchronous selectors (selectors on data not conforming
// to our async data format) as well as allowing the user to specify
// a state derivation function
if (arguments.length === 2 && typeof syncSelectors === 'function') {
compute = syncSelectors
syncSelectors = {}
}
// Create selectors functions from the user supplied maps
const getAsync = createStructuredSelector(asyncSelectors)
const getSync = createStructuredSelector(syncSelectors)
// We’ll store the async data key names to use later
const asyncKeys = Object.keys(asyncSelectors)
return createSelector(
getAsync,
getSync,
(asyncData, syncData) => {
// Let’s compute! What we want to do is iterate through all slices of
// async data (these { loading, loaded, data } objects), and derive
// their total loading state. Remember, we have 4 possible states:
// uninitiated, loading, completed, and errored
// We say that, if one async data slice is uninitiated,
// all are uninitiated
if (
asyncKeys.some(key => (
!asyncData[key].loading &&
!asyncData[key].loaded &&
!asyncData[key].error
))
) {
return { loading: false, loaded: false, data: null }
}
// Likewise, if one is loading, they are all loading
if (asyncKeys.some(key => asyncData[key].loading)) {
return { loading: true, loaded: false, data: null }
}
// And the same for errors
if (asyncKeys.some(key => asyncData[key].error)) {
return {
loading: false,
loaded: false,
error: true // TODO We should probably put a list of errors here
}
}
// Otherwise we’re ready to run the selector!
return {
loading: false,
loaded: true,
data: compute({
// Merge in the sync slices so that the compute function may also
// take them into account
...syncData,
// We also of course merge in the async slices. Here, however, we want
// to transform them into just their `data` payloads so that the
// compute function can operate on the data alone without worrying
// about load state
...asyncKeys.reduce((acc, key) => {
acc[key] = asyncData[key].data
return acc
})
})
}
}
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment