Open alex-dixon opened 5 years ago
latest remote-dm master
import { lastInputOutputMemoize } from './reselect-raw'
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 makeSelectorCreator(memoize, outerMemoize, ...memoizeOptions) {
return (...funcs) => {
let recomputations = 0
const resultFunc = funcs.pop()
const dependencies = getDependencies(funcs)
// If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
const memoizedResultFunc = memoize(function() {
recomputations++
// apply arguments instead of spreading for performance.
return resultFunc.apply(null, arguments)
}, ...memoizeOptions)
const memoizeDb = outerMemoize || memoize
// this is the selector itself -- we don't pass in anything but a DB bag to our selectors...
const selector = memoizeDb(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.dependencies = dependencies
selector.recomputations = () => recomputations
selector.resetRecomputations = () => (recomputations = 0)
return selector
}
}
export const createSelector = makeSelectorCreator(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
}, {})
})
}
/**
* Stateful selectors with an "accumulator bag"
*/
const defaultIs = (a, b) =>
areArgumentsShallowlyEqual(defaultEqualityCheck, a, b)
/*
* experiment # 2 in fancy diff selectors
* return in the form [acc, item]
* */
export function accumulatorMemoize(func) {
let lastArgs = null
let lastResult = null
let acc = {}
return function() {
if (!defaultIs(lastArgs, arguments)) {
const fullArgs = [acc].concat(Array.from(arguments))
const ret = func.apply(null, fullArgs)
if (!ret)
throw new Error(
'Stateful selectors must return a stream accumulator as first argument'
)
acc = ret[0]
lastResult = ret[1]
}
lastArgs = arguments
return lastResult
}
}
export const createStatefulSelector = makeSelectorCreator(
accumulatorMemoize,
defaultMemoize,
{ diff: true }
)
exact same as latest reselect, except makeSelectorCreator takes two different memoization functions instead of one
Removing as release blocker
@mikegai Let me know the best place to get this from