-
Notifications
You must be signed in to change notification settings - Fork 2k
/
index.js
107 lines (92 loc) · 3.57 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
/**
* External dependencies
*/
import memoize from 'lodash/memoize';
import shallowEqual from 'react-pure-render/shallowEqual';
/**
* Constants
*/
/**
* Defines acceptable argument types for a memoized selector when using the
* default cache key generating function.
*
* @type {Array}
*/
const VALID_ARG_TYPES = [ 'number', 'boolean', 'string' ];
/**
* Default behavior for determining whether current state differs from previous
* state, which is the basis upon which memoize cache is cleared. Should return
* a value or array of values to be shallowly compared for strict equality.
*
* @type {Function}
* @param {Object} state Current state object
* @return {(Array|*)} Value(s) to be shallow compared
*/
const DEFAULT_GET_DEPENDANTS = ( state ) => state;
/**
* At runtime, assigns a function which returns a cache key for the memoized
* selector function, given a state object and a variable set of arguments. In
* development mode, this warns when the memoized selector is passed a complex
* object argument, as these cannot be depended upon as reliable cache keys.
*
* @type {Function} Function returning cache key for memoized selector
*/
const DEFAULT_GET_CACHE_KEY = ( () => {
let warn, includes;
if ( 'production' !== process.env.NODE_ENV ) {
// Webpack can optimize bundles if it can detect that a block will
// never be reached. Since `NODE_ENV` is defined using DefinePlugin,
// these debugging modules will be excluded from the production build.
warn = require( 'lib/warn' );
includes = require( 'lodash/includes' );
} else {
return ( state, ...args ) => args.join();
}
return ( state, ...args ) => {
const hasInvalidArg = args.some( ( arg ) => {
return arg && ! includes( VALID_ARG_TYPES, typeof arg );
} );
if ( hasInvalidArg ) {
warn( 'Do not pass complex objects as arguments for a memoized selector' );
}
return args.join();
};
} )();
/**
* Given an array of getDependants functions, returns a single function which,
* when called, returns an array of mapped results from those functions.
*
* @param {Function[]} dependants Array of getDependants
* @return {Function} Function mapping getDependants results
*/
const makeSelectorFromArray = ( dependants ) =>
( state, ...args ) => dependants.map( dependant =>
dependant( state, ...args ) );
/**
* Returns a memoized state selector for use with the global application state.
*
* @param {Function} selector Function calculating cached result
* @param {Function|Function[]} getDependants Function(s) describing dependent
* state, or an array of dependent
* state selectors
* @param {Function} getCacheKey Function generating cache key
* @return {Function} Memoized selector
*/
export default function createSelector( selector, getDependants = DEFAULT_GET_DEPENDANTS, getCacheKey = DEFAULT_GET_CACHE_KEY ) {
const memoizedSelector = memoize( selector, getCacheKey );
let lastDependants;
if ( Array.isArray( getDependants ) ) {
getDependants = makeSelectorFromArray( getDependants );
}
return Object.assign( function( state, ...args ) {
let currentDependants = getDependants( state, ...args );
if ( ! Array.isArray( currentDependants ) ) {
currentDependants = [ currentDependants ];
}
if ( lastDependants && ! shallowEqual( currentDependants, lastDependants ) ) {
memoizedSelector.cache.clear();
}
lastDependants = currentDependants;
return memoizedSelector( state, ...args );
}, { memoizedSelector } );
}