source: node_modules/reselect/src/createSelectorCreator.ts

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 19.2 KB
RevLine 
[d24f17c]1import { weakMapMemoize } from './weakMapMemoize'
2
3import type {
4 Combiner,
5 CreateSelectorOptions,
6 DropFirstParameter,
7 ExtractMemoizerFields,
8 GetParamsFromSelectors,
9 GetStateFromSelectors,
10 InterruptRecursion,
11 OutputSelector,
12 Selector,
13 SelectorArray,
14 SetRequired,
15 Simplify,
16 UnknownMemoizer
17} from './types'
18
19import {
20 assertIsFunction,
21 collectInputSelectorResults,
22 ensureIsArray,
23 getDependencies,
24 getDevModeChecksExecutionInfo
25} from './utils'
26
27/**
28 * An instance of `createSelector`, customized with a given memoize implementation.
29 *
30 * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
31 * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
32 * @template StateType - The type of state that the selectors created with this selector creator will operate on.
33 *
34 * @public
35 */
36export interface CreateSelectorFunction<
37 MemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
38 ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize,
39 StateType = any
40> {
41 /**
42 * Creates a memoized selector function.
43 *
44 * @param createSelectorArgs - An arbitrary number of input selectors as separate inline arguments and a `combiner` function.
45 * @returns A memoized output selector.
46 *
47 * @template InputSelectors - The type of the input selectors as an array.
48 * @template Result - The return type of the `combiner` as well as the output selector.
49 * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
50 * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
51 *
52 * @see {@link https://reselect.js.org/api/createselector `createSelector`}
53 */
54 <InputSelectors extends SelectorArray<StateType>, Result>(
55 ...createSelectorArgs: [
56 ...inputSelectors: InputSelectors,
57 combiner: Combiner<InputSelectors, Result>
58 ]
59 ): OutputSelector<
60 InputSelectors,
61 Result,
62 MemoizeFunction,
63 ArgsMemoizeFunction
64 > &
65 InterruptRecursion
66
67 /**
68 * Creates a memoized selector function.
69 *
70 * @param createSelectorArgs - An arbitrary number of input selectors as separate inline arguments, a `combiner` function and an `options` object.
71 * @returns A memoized output selector.
72 *
73 * @template InputSelectors - The type of the input selectors as an array.
74 * @template Result - The return type of the `combiner` as well as the output selector.
75 * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
76 * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
77 *
78 * @see {@link https://reselect.js.org/api/createselector `createSelector`}
79 */
80 <
81 InputSelectors extends SelectorArray<StateType>,
82 Result,
83 OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
84 OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
85 >(
86 ...createSelectorArgs: [
87 ...inputSelectors: InputSelectors,
88 combiner: Combiner<InputSelectors, Result>,
89 createSelectorOptions: Simplify<
90 CreateSelectorOptions<
91 MemoizeFunction,
92 ArgsMemoizeFunction,
93 OverrideMemoizeFunction,
94 OverrideArgsMemoizeFunction
95 >
96 >
97 ]
98 ): OutputSelector<
99 InputSelectors,
100 Result,
101 OverrideMemoizeFunction,
102 OverrideArgsMemoizeFunction
103 > &
104 InterruptRecursion
105
106 /**
107 * Creates a memoized selector function.
108 *
109 * @param inputSelectors - An array of input selectors.
110 * @param combiner - A function that Combines the input selectors and returns an output selector. Otherwise known as the result function.
111 * @param createSelectorOptions - An optional options object that allows for further customization per selector.
112 * @returns A memoized output selector.
113 *
114 * @template InputSelectors - The type of the input selectors array.
115 * @template Result - The return type of the `combiner` as well as the output selector.
116 * @template OverrideMemoizeFunction - The type of the optional `memoize` function that could be passed into the options object to override the original `memoize` function that was initially passed into `createSelectorCreator`.
117 * @template OverrideArgsMemoizeFunction - The type of the optional `argsMemoize` function that could be passed into the options object to override the original `argsMemoize` function that was initially passed into `createSelectorCreator`.
118 *
119 * @see {@link https://reselect.js.org/api/createselector `createSelector`}
120 */
121 <
122 InputSelectors extends SelectorArray<StateType>,
123 Result,
124 OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
125 OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
126 >(
127 inputSelectors: [...InputSelectors],
128 combiner: Combiner<InputSelectors, Result>,
129 createSelectorOptions?: Simplify<
130 CreateSelectorOptions<
131 MemoizeFunction,
132 ArgsMemoizeFunction,
133 OverrideMemoizeFunction,
134 OverrideArgsMemoizeFunction
135 >
136 >
137 ): OutputSelector<
138 InputSelectors,
139 Result,
140 OverrideMemoizeFunction,
141 OverrideArgsMemoizeFunction
142 > &
143 InterruptRecursion
144
145 /**
146 * Creates a "pre-typed" version of {@linkcode createSelector createSelector}
147 * where the `state` type is predefined.
148 *
149 * This allows you to set the `state` type once, eliminating the need to
150 * specify it with every {@linkcode createSelector createSelector} call.
151 *
152 * @returns A pre-typed `createSelector` with the state type already defined.
153 *
154 * @example
155 * ```ts
156 * import { createSelector } from 'reselect'
157 *
158 * export interface RootState {
159 * todos: { id: number; completed: boolean }[]
160 * alerts: { id: number; read: boolean }[]
161 * }
162 *
163 * export const createAppSelector = createSelector.withTypes<RootState>()
164 *
165 * const selectTodoIds = createAppSelector(
166 * [
167 * // Type of `state` is set to `RootState`, no need to manually set the type
168 * state => state.todos
169 * ],
170 * todos => todos.map(({ id }) => id)
171 * )
172 * ```
173 * @template OverrideStateType - The specific type of state used by all selectors created with this selector creator.
174 *
175 * @see {@link https://reselect.js.org/api/createselector#defining-a-pre-typed-createselector `createSelector.withTypes`}
176 *
177 * @since 5.1.0
178 */
179 withTypes: <OverrideStateType extends StateType>() => CreateSelectorFunction<
180 MemoizeFunction,
181 ArgsMemoizeFunction,
182 OverrideStateType
183 >
184}
185
186/**
187 * Creates a selector creator function with the specified memoization function
188 * and options for customizing memoization behavior.
189 *
190 * @param options - An options object containing the `memoize` function responsible for memoizing the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). It also provides additional options for customizing memoization. While the `memoize` property is mandatory, the rest are optional.
191 * @returns A customized `createSelector` function.
192 *
193 * @example
194 * ```ts
195 * const customCreateSelector = createSelectorCreator({
196 * memoize: customMemoize, // Function to be used to memoize `resultFunc`
197 * memoizeOptions: [memoizeOption1, memoizeOption2], // Options passed to `customMemoize` as the second argument onwards
198 * argsMemoize: customArgsMemoize, // Function to be used to memoize the selector's arguments
199 * argsMemoizeOptions: [argsMemoizeOption1, argsMemoizeOption2] // Options passed to `customArgsMemoize` as the second argument onwards
200 * })
201 *
202 * const customSelector = customCreateSelector(
203 * [inputSelector1, inputSelector2],
204 * resultFunc // `resultFunc` will be passed as the first argument to `customMemoize`
205 * )
206 *
207 * customSelector(
208 * ...selectorArgs // Will be memoized by `customArgsMemoize`
209 * )
210 * ```
211 *
212 * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
213 * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
214 *
215 * @see {@link https://reselect.js.org/api/createSelectorCreator#using-options-since-500 `createSelectorCreator`}
216 *
217 * @since 5.0.0
218 * @public
219 */
220export function createSelectorCreator<
221 MemoizeFunction extends UnknownMemoizer,
222 ArgsMemoizeFunction extends UnknownMemoizer = typeof weakMapMemoize
223>(
224 options: Simplify<
225 SetRequired<
226 CreateSelectorOptions<
227 typeof weakMapMemoize,
228 typeof weakMapMemoize,
229 MemoizeFunction,
230 ArgsMemoizeFunction
231 >,
232 'memoize'
233 >
234 >
235): CreateSelectorFunction<MemoizeFunction, ArgsMemoizeFunction>
236
237/**
238 * Creates a selector creator function with the specified memoization function
239 * and options for customizing memoization behavior.
240 *
241 * @param memoize - The `memoize` function responsible for memoizing the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
242 * @param memoizeOptionsFromArgs - Optional configuration options for the memoization function. These options are then passed to the memoize function as the second argument onwards.
243 * @returns A customized `createSelector` function.
244 *
245 * @example
246 * ```ts
247 * const customCreateSelector = createSelectorCreator(customMemoize, // Function to be used to memoize `resultFunc`
248 * option1, // Will be passed as second argument to `customMemoize`
249 * option2, // Will be passed as third argument to `customMemoize`
250 * option3 // Will be passed as fourth argument to `customMemoize`
251 * )
252 *
253 * const customSelector = customCreateSelector(
254 * [inputSelector1, inputSelector2],
255 * resultFunc // `resultFunc` will be passed as the first argument to `customMemoize`
256 * )
257 * ```
258 *
259 * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
260 *
261 * @see {@link https://reselect.js.org/api/createSelectorCreator#using-memoize-and-memoizeoptions `createSelectorCreator`}
262 *
263 * @public
264 */
265export function createSelectorCreator<MemoizeFunction extends UnknownMemoizer>(
266 memoize: MemoizeFunction,
267 ...memoizeOptionsFromArgs: DropFirstParameter<MemoizeFunction>
268): CreateSelectorFunction<MemoizeFunction>
269
270/**
271 * Creates a selector creator function with the specified memoization
272 * function and options for customizing memoization behavior.
273 *
274 * @param memoizeOrOptions - Either A `memoize` function or an `options` object containing the `memoize` function.
275 * @param memoizeOptionsFromArgs - Optional configuration options for the memoization function. These options are then passed to the memoize function as the second argument onwards.
276 * @returns A customized `createSelector` function.
277 *
278 * @template MemoizeFunction - The type of the memoize function that is used to memoize the `resultFunc` inside `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`).
279 * @template ArgsMemoizeFunction - The type of the optional memoize function that is used to memoize the arguments passed into the output selector generated by `createSelector` (e.g., `lruMemoize` or `weakMapMemoize`). If none is explicitly provided, `weakMapMemoize` will be used.
280 * @template MemoizeOrOptions - The type of the first argument. It can either be a `memoize` function or an `options` object containing the `memoize` function.
281 */
282export function createSelectorCreator<
283 MemoizeFunction extends UnknownMemoizer,
284 ArgsMemoizeFunction extends UnknownMemoizer,
285 MemoizeOrOptions extends
286 | MemoizeFunction
287 | SetRequired<
288 CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
289 'memoize'
290 >
291>(
292 memoizeOrOptions: MemoizeOrOptions,
293 ...memoizeOptionsFromArgs: MemoizeOrOptions extends SetRequired<
294 CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
295 'memoize'
296 >
297 ? never
298 : DropFirstParameter<MemoizeFunction>
299) {
300 /** options initially passed into `createSelectorCreator`. */
301 const createSelectorCreatorOptions: SetRequired<
302 CreateSelectorOptions<MemoizeFunction, ArgsMemoizeFunction>,
303 'memoize'
304 > = typeof memoizeOrOptions === 'function'
305 ? {
306 memoize: memoizeOrOptions as MemoizeFunction,
307 memoizeOptions: memoizeOptionsFromArgs
308 }
309 : memoizeOrOptions
310
311 const createSelector = <
312 InputSelectors extends SelectorArray,
313 Result,
314 OverrideMemoizeFunction extends UnknownMemoizer = MemoizeFunction,
315 OverrideArgsMemoizeFunction extends UnknownMemoizer = ArgsMemoizeFunction
316 >(
317 ...createSelectorArgs: [
318 ...inputSelectors: [...InputSelectors],
319 combiner: Combiner<InputSelectors, Result>,
320 createSelectorOptions?: CreateSelectorOptions<
321 MemoizeFunction,
322 ArgsMemoizeFunction,
323 OverrideMemoizeFunction,
324 OverrideArgsMemoizeFunction
325 >
326 ]
327 ) => {
328 let recomputations = 0
329 let dependencyRecomputations = 0
330 let lastResult: Result
331
332 // Due to the intricacies of rest params, we can't do an optional arg after `...createSelectorArgs`.
333 // So, start by declaring the default value here.
334 // (And yes, the words 'memoize' and 'options' appear too many times in this next sequence.)
335 let directlyPassedOptions: CreateSelectorOptions<
336 MemoizeFunction,
337 ArgsMemoizeFunction,
338 OverrideMemoizeFunction,
339 OverrideArgsMemoizeFunction
340 > = {}
341
342 // Normally, the result func or "combiner" is the last arg
343 let resultFunc = createSelectorArgs.pop() as
344 | Combiner<InputSelectors, Result>
345 | CreateSelectorOptions<
346 MemoizeFunction,
347 ArgsMemoizeFunction,
348 OverrideMemoizeFunction,
349 OverrideArgsMemoizeFunction
350 >
351
352 // If the result func is actually an _object_, assume it's our options object
353 if (typeof resultFunc === 'object') {
354 directlyPassedOptions = resultFunc
355 // and pop the real result func off
356 resultFunc = createSelectorArgs.pop() as Combiner<InputSelectors, Result>
357 }
358
359 assertIsFunction(
360 resultFunc,
361 `createSelector expects an output function after the inputs, but received: [${typeof resultFunc}]`
362 )
363
364 // Determine which set of options we're using. Prefer options passed directly,
365 // but fall back to options given to `createSelectorCreator`.
366 const combinedOptions = {
367 ...createSelectorCreatorOptions,
368 ...directlyPassedOptions
369 }
370
371 const {
372 memoize,
373 memoizeOptions = [],
374 argsMemoize = weakMapMemoize,
375 argsMemoizeOptions = [],
376 devModeChecks = {}
377 } = combinedOptions
378
379 // Simplifying assumption: it's unlikely that the first options arg of the provided memoizer
380 // is an array. In most libs I've looked at, it's an equality function or options object.
381 // Based on that, if `memoizeOptions` _is_ an array, we assume it's a full
382 // user-provided array of options. Otherwise, it must be just the _first_ arg, and so
383 // we wrap it in an array so we can apply it.
384 const finalMemoizeOptions = ensureIsArray(memoizeOptions)
385 const finalArgsMemoizeOptions = ensureIsArray(argsMemoizeOptions)
386 const dependencies = getDependencies(createSelectorArgs) as InputSelectors
387
388 const memoizedResultFunc = memoize(function recomputationWrapper() {
389 recomputations++
390 // apply arguments instead of spreading for performance.
391 // @ts-ignore
392 return (resultFunc as Combiner<InputSelectors, Result>).apply(
393 null,
394 arguments
395 )
396 }, ...finalMemoizeOptions) as Combiner<InputSelectors, Result> &
397 ExtractMemoizerFields<OverrideMemoizeFunction>
398
399 let firstRun = true
400
401 // If a selector is called with the exact same arguments we don't need to traverse our dependencies again.
402 const selector = argsMemoize(function dependenciesChecker() {
403 dependencyRecomputations++
404 /** Return values of input selectors which the `resultFunc` takes as arguments. */
405 const inputSelectorResults = collectInputSelectorResults(
406 dependencies,
407 arguments
408 )
409
410 // apply arguments instead of spreading for performance.
411 // @ts-ignore
412 lastResult = memoizedResultFunc.apply(null, inputSelectorResults)
413
414 if (process.env.NODE_ENV !== 'production') {
415 const { identityFunctionCheck, inputStabilityCheck } =
416 getDevModeChecksExecutionInfo(firstRun, devModeChecks)
417 if (identityFunctionCheck.shouldRun) {
418 identityFunctionCheck.run(
419 resultFunc as Combiner<InputSelectors, Result>,
420 inputSelectorResults,
421 lastResult
422 )
423 }
424
425 if (inputStabilityCheck.shouldRun) {
426 // make a second copy of the params, to check if we got the same results
427 const inputSelectorResultsCopy = collectInputSelectorResults(
428 dependencies,
429 arguments
430 )
431
432 inputStabilityCheck.run(
433 { inputSelectorResults, inputSelectorResultsCopy },
434 { memoize, memoizeOptions: finalMemoizeOptions },
435 arguments
436 )
437 }
438
439 if (firstRun) firstRun = false
440 }
441
442 return lastResult
443 }, ...finalArgsMemoizeOptions) as unknown as Selector<
444 GetStateFromSelectors<InputSelectors>,
445 Result,
446 GetParamsFromSelectors<InputSelectors>
447 > &
448 ExtractMemoizerFields<OverrideArgsMemoizeFunction>
449
450 return Object.assign(selector, {
451 resultFunc,
452 memoizedResultFunc,
453 dependencies,
454 dependencyRecomputations: () => dependencyRecomputations,
455 resetDependencyRecomputations: () => {
456 dependencyRecomputations = 0
457 },
458 lastResult: () => lastResult,
459 recomputations: () => recomputations,
460 resetRecomputations: () => {
461 recomputations = 0
462 },
463 memoize,
464 argsMemoize
465 }) as OutputSelector<
466 InputSelectors,
467 Result,
468 OverrideMemoizeFunction,
469 OverrideArgsMemoizeFunction
470 >
471 }
472
473 Object.assign(createSelector, {
474 withTypes: () => createSelector
475 })
476
477 return createSelector as CreateSelectorFunction<
478 MemoizeFunction,
479 ArgsMemoizeFunction
480 >
481}
482
483/**
484 * Accepts one or more "input selectors" (either as separate arguments or a single array),
485 * a single "result function" / "combiner", and an optional options object, and
486 * generates a memoized selector function.
487 *
488 * @see {@link https://reselect.js.org/api/createSelector `createSelector`}
489 *
490 * @public
491 */
492export const createSelector =
493 /* #__PURE__ */ createSelectorCreator(weakMapMemoize)
Note: See TracBrowser for help on using the repository browser.