Created
December 1, 2017 16:02
-
-
Save sfrdmn/7f38b6d264b9af79259f0da2784c99bd to your computer and use it in GitHub Desktop.
mcr_post_13
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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