forked from zhurui/management
108 lines
3.2 KiB
JavaScript
108 lines
3.2 KiB
JavaScript
function defaultEqualityCheck(a, b) {
|
|
return a === b
|
|
}
|
|
|
|
function areArgumentsShallowlyEqual(equalityCheck, prev, next) {
|
|
if (prev === null || next === null || prev.length !== next.length) {
|
|
return false
|
|
}
|
|
|
|
// Do this in a for loop (and not a `forEach` or an `every`) so we can determine equality as fast as possible.
|
|
const length = prev.length
|
|
for (let i = 0; i < length; i++) {
|
|
if (!equalityCheck(prev[i], next[i])) {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
export function defaultMemoize(func, equalityCheck = defaultEqualityCheck) {
|
|
let lastArgs = null
|
|
let lastResult = null
|
|
// we reference arguments instead of spreading them for performance reasons
|
|
return function () {
|
|
if (!areArgumentsShallowlyEqual(equalityCheck, lastArgs, arguments)) {
|
|
// apply arguments instead of spreading for performance.
|
|
lastResult = func.apply(null, arguments)
|
|
}
|
|
|
|
lastArgs = arguments
|
|
return lastResult
|
|
}
|
|
}
|
|
|
|
function getDependencies(funcs) {
|
|
const dependencies = Array.isArray(funcs[0]) ? funcs[0] : funcs
|
|
|
|
if (!dependencies.every(dep => typeof dep === 'function')) {
|
|
const dependencyTypes = dependencies.map(
|
|
dep => typeof dep
|
|
).join(', ')
|
|
throw new Error(
|
|
'Selector creators expect all input-selectors to be functions, ' +
|
|
`instead received the following types: [${dependencyTypes}]`
|
|
)
|
|
}
|
|
|
|
return dependencies
|
|
}
|
|
|
|
export function createSelectorCreator(memoize, ...memoizeOptions) {
|
|
return (...funcs) => {
|
|
let recomputations = 0
|
|
const resultFunc = funcs.pop()
|
|
const dependencies = getDependencies(funcs)
|
|
|
|
const memoizedResultFunc = memoize(
|
|
function () {
|
|
recomputations++
|
|
// apply arguments instead of spreading for performance.
|
|
return resultFunc.apply(null, arguments)
|
|
},
|
|
...memoizeOptions
|
|
)
|
|
|
|
// If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
|
|
const selector = defaultMemoize(function () {
|
|
const params = []
|
|
const length = dependencies.length
|
|
|
|
for (let i = 0; i < length; i++) {
|
|
// apply arguments instead of spreading and mutate a local list of params for performance.
|
|
params.push(dependencies[i].apply(null, arguments))
|
|
}
|
|
|
|
// apply arguments instead of spreading for performance.
|
|
return memoizedResultFunc.apply(null, params)
|
|
})
|
|
|
|
selector.resultFunc = resultFunc
|
|
selector.recomputations = () => recomputations
|
|
selector.resetRecomputations = () => recomputations = 0
|
|
return selector
|
|
}
|
|
}
|
|
|
|
export const createSelector = createSelectorCreator(defaultMemoize)
|
|
|
|
export function createStructuredSelector(selectors, selectorCreator = createSelector) {
|
|
if (typeof selectors !== 'object') {
|
|
throw new Error(
|
|
'createStructuredSelector expects first argument to be an object ' +
|
|
`where each property is a selector, instead received a ${typeof selectors}`
|
|
)
|
|
}
|
|
const objectKeys = Object.keys(selectors)
|
|
return selectorCreator(
|
|
objectKeys.map(key => selectors[key]),
|
|
(...values) => {
|
|
return values.reduce((composition, value, index) => {
|
|
composition[objectKeys[index]] = value
|
|
return composition
|
|
}, {})
|
|
}
|
|
)
|
|
}
|