reselect-kit 1.0.1 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +307 -36
- package/dist/cjs/reselectKit.development.cjs +256 -95
- package/dist/cjs/reselectKit.development.cjs.map +1 -1
- package/dist/cjs/reselectKit.production.min.cjs +1 -1
- package/dist/cjs/reselectKit.production.min.cjs.map +1 -1
- package/dist/reselectKit.d.ts +201 -92
- package/dist/reselectKit.mjs +257 -97
- package/dist/reselectKit.mjs.map +1 -1
- package/package.json +6 -6
- package/src/__tests__/createAdaptedSelector.test.ts +3 -4
- package/src/__tests__/createBoundSelector.test.ts +98 -5
- package/src/__tests__/createChainSelector.bench.ts +117 -0
- package/src/__tests__/createChainSelector.test.ts +162 -6
- package/src/__tests__/createPathSelector.test.ts +14 -0
- package/src/__tests__/createSequenceSelector.test.ts +10 -14
- package/src/_helpers/__tests__/generateSelectorKey.test.ts +24 -0
- package/src/_helpers/__tests__/getSelectorName.test.ts +30 -0
- package/src/_helpers/unfreezeProps.ts +14 -0
- package/src/_hooks/__tests__/useSelector.bench.ts +133 -0
- package/src/_hooks/__tests__/useSelector.test.ts +69 -0
- package/src/_hooks/__tests__/useSelectors.bench.ts +160 -0
- package/src/_hooks/__tests__/useSelectors.test.ts +219 -0
- package/src/_hooks/selectorOptions.ts +11 -0
- package/src/_hooks/useSelector.ts +68 -26
- package/src/_hooks/useSelectors.ts +162 -0
- package/src/cache/__tests__/intervalMapCache.test.ts +11 -0
- package/src/createAdaptedSelector.ts +9 -5
- package/src/createBoundSelector.ts +92 -84
- package/src/createCachedSequenceSelector.ts +12 -8
- package/src/createCachedStructuredSelector.ts +11 -20
- package/src/createChainSelector.ts +134 -89
- package/src/createPathSelector.ts +71 -104
- package/src/createPropSelector.ts +9 -8
- package/src/createSequenceSelector.ts +14 -9
- package/src/index.ts +2 -0
- package/src/keys/createKeySelectorComposer.ts +12 -23
- package/src/types.ts +115 -7
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
let _veksa_re_reselect = require("@veksa/re-reselect");
|
|
3
2
|
let _veksa_reselect = require("@veksa/reselect");
|
|
3
|
+
let _veksa_re_reselect = require("@veksa/re-reselect");
|
|
4
4
|
let _veksa_react_redux = require("@veksa/react-redux");
|
|
5
5
|
let react = require("react");
|
|
6
6
|
//#region src/createSegmentSelector.ts
|
|
@@ -85,6 +85,15 @@ const innerCreatePathSelector = (baseSelector, path = [], applyMeta = defaultVoi
|
|
|
85
85
|
return innerCreatePathSelector(baseSelector, [...path, String(key)], applyMeta);
|
|
86
86
|
} });
|
|
87
87
|
};
|
|
88
|
+
/**
|
|
89
|
+
* Builds a proxy that turns property access into a selector reading that path.
|
|
90
|
+
*
|
|
91
|
+
* `Params` is inferred straight from the base selector, so the plain and the
|
|
92
|
+
* parametric case share one signature — and one set of builder types — instead
|
|
93
|
+
* of being told apart by a conditional in the parameter position. It defaults
|
|
94
|
+
* to empty so that `createPathSelector<State, Result>(selector)` keeps working
|
|
95
|
+
* for callers that spell the type arguments out.
|
|
96
|
+
*/
|
|
88
97
|
function createPathSelector(baseSelector) {
|
|
89
98
|
return innerCreatePathSelector(baseSelector);
|
|
90
99
|
}
|
|
@@ -251,15 +260,54 @@ const generateBindingName = (binding) => {
|
|
|
251
260
|
}), {});
|
|
252
261
|
return `${Object.keys(structure).join()} -> ${Object.values(structure).join()}`;
|
|
253
262
|
};
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
/**
|
|
264
|
+
* The default binding strategy: applies the binding to the props the caller
|
|
265
|
+
* passed, hands them to the base selector, and puts them back as they were.
|
|
266
|
+
*
|
|
267
|
+
* Almost every binding is a single key — `bound(selector, { positionId })` —
|
|
268
|
+
* and specialising it removes the two allocations the generic path costs per
|
|
269
|
+
* call: the `{ result, rollback }` object and the `rollback` closure. On a
|
|
270
|
+
* graph with a bound selector per row those are the calls that dominate.
|
|
271
|
+
*
|
|
272
|
+
* The `delete` in the restore stays deliberately. Leaving the key behind as
|
|
273
|
+
* `undefined` would be cheaper — it avoids putting the caller's props object
|
|
274
|
+
* into dictionary mode — but it also changes the object's shape, and
|
|
275
|
+
* `useSelector` memoizes props shallowly: a props object that has grown an
|
|
276
|
+
* extra key never again compares equal to a freshly built one, so the
|
|
277
|
+
* memoization the binding exists to protect would be defeated.
|
|
278
|
+
*/
|
|
279
|
+
const applyBinding = (baseSelector, binding) => {
|
|
280
|
+
const source = binding;
|
|
281
|
+
const keys = Object.keys(source);
|
|
282
|
+
if (keys.length === 1) {
|
|
283
|
+
const key = keys[0];
|
|
284
|
+
const value = source[key];
|
|
285
|
+
return (state, props) => {
|
|
286
|
+
const target = props;
|
|
287
|
+
if (typeof target !== "object" || target === null) return baseSelector(state, source);
|
|
288
|
+
const previous = target[key];
|
|
289
|
+
if (previous === value) return baseSelector(state, props);
|
|
290
|
+
const had = Object.prototype.hasOwnProperty.call(target, key);
|
|
291
|
+
target[key] = value;
|
|
292
|
+
try {
|
|
293
|
+
return baseSelector(state, props);
|
|
294
|
+
} finally {
|
|
295
|
+
if (had) target[key] = previous;
|
|
296
|
+
else delete target[key];
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
return (state, props) => {
|
|
301
|
+
const { result: nextProps, rollback } = temporaryAssign(props, source);
|
|
302
|
+
try {
|
|
303
|
+
return baseSelector(state, nextProps);
|
|
304
|
+
} finally {
|
|
305
|
+
rollback();
|
|
306
|
+
}
|
|
307
|
+
};
|
|
260
308
|
};
|
|
261
|
-
const
|
|
262
|
-
const bindingStrategy = options.bindingStrategy ??
|
|
309
|
+
const createBoundSelector = (baseSelector, binding, options = {}) => {
|
|
310
|
+
const bindingStrategy = options.bindingStrategy ?? applyBinding;
|
|
263
311
|
const keySelectorComposer = options.keySelectorComposer ?? stringComposeKeySelectors;
|
|
264
312
|
const boundSelector = bindingStrategy(baseSelector, binding);
|
|
265
313
|
Object.assign(boundSelector, baseSelector);
|
|
@@ -286,49 +334,6 @@ const createBoundInnerSelector = (baseSelector, binding, options = {}) => {
|
|
|
286
334
|
}
|
|
287
335
|
return boundSelector;
|
|
288
336
|
};
|
|
289
|
-
const createBoundSelector = (baseSelector, binding) => {
|
|
290
|
-
return createBoundInnerSelector(baseSelector, binding, { bindingStrategy: (selector, bindingProps) => {
|
|
291
|
-
const source = bindingProps;
|
|
292
|
-
const keys = Object.keys(source);
|
|
293
|
-
/**
|
|
294
|
-
* Almost every binding is a single key — `bound(selector, { positionId })` —
|
|
295
|
-
* and specialising it removes the two allocations the generic path costs per
|
|
296
|
-
* call: the `{ result, rollback }` object and the `rollback` closure. On a
|
|
297
|
-
* graph with a bound selector per row those are the calls that dominate.
|
|
298
|
-
*
|
|
299
|
-
* The `delete` in the restore stays deliberately. Leaving the key behind as
|
|
300
|
-
* `undefined` would be cheaper — it avoids putting the caller's props object
|
|
301
|
-
* into dictionary mode — but it also changes the object's shape, and
|
|
302
|
-
* `useSelector` memoizes props shallowly: a props object that has grown an
|
|
303
|
-
* extra key never again compares equal to a freshly built one, so the
|
|
304
|
-
* memoization the binding exists to protect would be defeated.
|
|
305
|
-
*/
|
|
306
|
-
if (keys.length === 1) {
|
|
307
|
-
const key = keys[0];
|
|
308
|
-
const value = source[key];
|
|
309
|
-
return (state, props) => {
|
|
310
|
-
const target = props;
|
|
311
|
-
if (typeof target !== "object" || target === null) return selector(state, source);
|
|
312
|
-
const previous = target[key];
|
|
313
|
-
if (previous === value) return selector(state, props);
|
|
314
|
-
const had = Object.prototype.hasOwnProperty.call(target, key);
|
|
315
|
-
target[key] = value;
|
|
316
|
-
try {
|
|
317
|
-
return selector(state, props);
|
|
318
|
-
} finally {
|
|
319
|
-
if (had) target[key] = previous;
|
|
320
|
-
else delete target[key];
|
|
321
|
-
}
|
|
322
|
-
};
|
|
323
|
-
}
|
|
324
|
-
return (state, props) => {
|
|
325
|
-
const { result: nextProps, rollback } = temporaryAssign(props, source);
|
|
326
|
-
const result = selector(state, nextProps);
|
|
327
|
-
rollback();
|
|
328
|
-
return result;
|
|
329
|
-
};
|
|
330
|
-
} });
|
|
331
|
-
};
|
|
332
337
|
//#endregion
|
|
333
338
|
//#region src/createEmptySelector.ts
|
|
334
339
|
/**
|
|
@@ -357,6 +362,11 @@ const generateMappingName = (mapping) => {
|
|
|
357
362
|
const innerCreateAdaptedSelector = (baseSelector, mapping) => (state, props) => {
|
|
358
363
|
return baseSelector(state, mapping(props));
|
|
359
364
|
};
|
|
365
|
+
/**
|
|
366
|
+
* Rewrites the props a selector reads. Only the props argument changes: the
|
|
367
|
+
* adapted selector takes `[P2]` where the base took `[P1]`, which is the same
|
|
368
|
+
* argument tuple every other creator in the library carries.
|
|
369
|
+
*/
|
|
360
370
|
const createAdaptedSelector = (baseSelector, mapping) => {
|
|
361
371
|
const adaptedSelector = innerCreateAdaptedSelector(baseSelector, mapping);
|
|
362
372
|
Object.assign(adaptedSelector, baseSelector);
|
|
@@ -391,7 +401,7 @@ const generateSelectorKey = (selector) => {
|
|
|
391
401
|
//#endregion
|
|
392
402
|
//#region src/createChainSelector.ts
|
|
393
403
|
function createChainSelector(selector, options, prevChain) {
|
|
394
|
-
const
|
|
404
|
+
const createLevel = (fn, chainOptions, isDerivedKeyless) => {
|
|
395
405
|
const combinedOptions = {
|
|
396
406
|
...options,
|
|
397
407
|
...chainOptions
|
|
@@ -399,8 +409,17 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
399
409
|
const keySelector = isCachedSelector(selector) ? selector.keySelector : defaultKeySelector;
|
|
400
410
|
const { createSelectorOptions = () => ({}), keySelectorComposer = stringComposeKeySelectors } = combinedOptions;
|
|
401
411
|
const keySelectorCreator = createKeySelectorCreator(keySelectorComposer);
|
|
402
|
-
|
|
403
|
-
|
|
412
|
+
/**
|
|
413
|
+
* Nothing below this level is keyed, so the cache would hold a single entry
|
|
414
|
+
* for the lifetime of the chain and every call would still pay for the key
|
|
415
|
+
* computation and the lookup that guard it. A plain memoized selector is the
|
|
416
|
+
* same mapping — re-reselect under a constant key is one `createSelector`
|
|
417
|
+
* instance — without the layer.
|
|
418
|
+
*/
|
|
419
|
+
const isKeyless = keySelector === defaultKeySelector;
|
|
420
|
+
const selectorOptions = createSelectorOptions();
|
|
421
|
+
const higherOrderSelector = isKeyless ? (selectorOptions.selectorCreator ?? _veksa_reselect.createSelector)([selector], fn) : (0, _veksa_re_reselect.createCachedSelector)([selector], fn)({
|
|
422
|
+
...selectorOptions,
|
|
404
423
|
keySelector
|
|
405
424
|
});
|
|
406
425
|
withDebugName(higherOrderSelector, () => {
|
|
@@ -420,37 +439,65 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
420
439
|
return derivedSelector(state, props);
|
|
421
440
|
};
|
|
422
441
|
combinedSelector.dependencies = [higherOrderSelector];
|
|
423
|
-
combinedSelector.cache = higherOrderSelector.cache;
|
|
442
|
+
if (!isKeyless) combinedSelector.cache = higherOrderSelector.cache;
|
|
424
443
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
*
|
|
429
|
-
* its own key computation, its own cache lookup, and its own evaluation of
|
|
430
|
-
* `higherOrderSelector` — to produce a value that depends on nothing but the
|
|
431
|
-
* derived selector. A `WeakMap` keyed on that selector is the same mapping with
|
|
432
|
-
* none of the layer, and it shares across cache keys rather than per key.
|
|
433
|
-
*
|
|
434
|
-
* Note what is deliberately *not* memoized: the `(state, props)` pair. `props`
|
|
435
|
-
* is mutated in place by `createBoundSelector`, so two calls can pass the same
|
|
436
|
-
* object reference with different contents, and a reference-keyed cache would
|
|
437
|
-
* hand back a stale selector.
|
|
444
|
+
* Assigned through a local and written to the selector once. Assigning the
|
|
445
|
+
* property in both branches instead reads as two expando declarations of
|
|
446
|
+
* `keySelector` to TypeScript before 7, which rejects it as a duplicate
|
|
447
|
+
* identifier.
|
|
438
448
|
*/
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
};
|
|
449
|
+
let levelKeySelector;
|
|
450
|
+
if (isDerivedKeyless)
|
|
451
|
+
/**
|
|
452
|
+
* A `map` level derives a plain `() => output`, never a cached selector, so
|
|
453
|
+
* it contributes nothing to the composed key and the whole composition is
|
|
454
|
+
* known at build time. It is the same call the wrapper below makes, minus
|
|
455
|
+
* the derived selector — so the key is composed exactly as before, once,
|
|
456
|
+
* instead of on every key computation of every level above.
|
|
457
|
+
*/
|
|
458
|
+
levelKeySelector = keySelectorCreator({ inputSelectors: [higherOrderSelector] });
|
|
459
|
+
else {
|
|
460
|
+
/**
|
|
461
|
+
* The derived selector's key selector, memoized on the derived selector itself.
|
|
462
|
+
*
|
|
463
|
+
* This used to be a second `createCachedSelector` over `[higherOrderSelector]`,
|
|
464
|
+
* which meant every key computation paid a whole extra cached-selector layer —
|
|
465
|
+
* its own key computation, its own cache lookup, and its own evaluation of
|
|
466
|
+
* `higherOrderSelector` — to produce a value that depends on nothing but the
|
|
467
|
+
* derived selector. A `WeakMap` keyed on that selector is the same mapping with
|
|
468
|
+
* none of the layer, and it shares across cache keys rather than per key.
|
|
469
|
+
*
|
|
470
|
+
* Note what is deliberately *not* memoized: the `(state, props)` pair. `props`
|
|
471
|
+
* is mutated in place by `createBoundSelector`, so two calls can pass the same
|
|
472
|
+
* object reference with different contents, and a reference-keyed cache would
|
|
473
|
+
* hand back a stale selector.
|
|
474
|
+
*/
|
|
475
|
+
const derivedKeySelectors = /* @__PURE__ */ new WeakMap();
|
|
476
|
+
levelKeySelector = (state, props) => {
|
|
477
|
+
const derivedSelector = higherOrderSelector(state, props);
|
|
478
|
+
let derivedKeySelector = derivedKeySelectors.get(derivedSelector);
|
|
479
|
+
if (derivedKeySelector === void 0) {
|
|
480
|
+
derivedKeySelector = keySelectorCreator({ inputSelectors: [higherOrderSelector, derivedSelector] });
|
|
481
|
+
derivedKeySelectors.set(derivedSelector, derivedKeySelector);
|
|
482
|
+
}
|
|
483
|
+
return derivedKeySelector(state, props);
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* When nothing keyed is left the composition yields the default key, and
|
|
488
|
+
* leaving the property off is what lets the next level skip its cache too.
|
|
489
|
+
* The wrapper above is a fresh closure, so only a `map` level can land here
|
|
490
|
+
* with the default.
|
|
491
|
+
*/
|
|
492
|
+
if (levelKeySelector !== defaultKeySelector) combinedSelector.keySelector = levelKeySelector;
|
|
449
493
|
withDebugName(combinedSelector, () => {
|
|
450
494
|
return `${getSelectorName(selector)} (will be chained ${stringifyFunction(fn)})`;
|
|
451
495
|
});
|
|
452
496
|
return createChainSelector(combinedSelector, combinedOptions, Object.assign(fn, { parentChain: prevChain }));
|
|
453
497
|
};
|
|
498
|
+
const chain = (fn, chainOptions) => {
|
|
499
|
+
return createLevel(fn, chainOptions, false);
|
|
500
|
+
};
|
|
454
501
|
const map = (fn, mapOptions) => {
|
|
455
502
|
const mapSelector = (result) => {
|
|
456
503
|
const output = fn(result);
|
|
@@ -460,7 +507,7 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
460
507
|
});
|
|
461
508
|
return mapSelector;
|
|
462
509
|
};
|
|
463
|
-
return
|
|
510
|
+
return createLevel(mapSelector, mapOptions, true);
|
|
464
511
|
};
|
|
465
512
|
const build = () => {
|
|
466
513
|
return Object.assign(selector, { chainHierarchy: prevChain });
|
|
@@ -637,26 +684,138 @@ var TreeCache = class {
|
|
|
637
684
|
}
|
|
638
685
|
};
|
|
639
686
|
//#endregion
|
|
640
|
-
//#region src/
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
687
|
+
//#region src/_helpers/unfreezeProps.ts
|
|
688
|
+
/**
|
|
689
|
+
* Bound selectors write the binding into the props object in place and roll it
|
|
690
|
+
* back afterwards (see temporaryAssign.ts), which throws on a frozen target in
|
|
691
|
+
* strict mode. React freezes element props in development, and immer-based
|
|
692
|
+
* stores hand out frozen objects in production too, so the copy stays in both
|
|
693
|
+
* builds — the hooks only ever call this when props actually change.
|
|
694
|
+
*/
|
|
695
|
+
const unfreezeProps = (props) => {
|
|
696
|
+
if (typeof props !== "object" || props === null || !Object.isFrozen(props)) return props;
|
|
697
|
+
return Array.isArray(props) ? props.slice() : { ...props };
|
|
645
698
|
};
|
|
646
699
|
//#endregion
|
|
700
|
+
//#region src/_hooks/selectorOptions.ts
|
|
701
|
+
/**
|
|
702
|
+
* Hoisted: react-redux reads the options object on every render, so there is no
|
|
703
|
+
* reason to allocate a fresh one per call site per render. The dev-mode checks
|
|
704
|
+
* re-run the selector to compare results, which these hooks control themselves.
|
|
705
|
+
*/
|
|
706
|
+
const selectorOptions = { devModeChecks: {
|
|
707
|
+
stabilityCheck: "never",
|
|
708
|
+
identityFunctionCheck: "never"
|
|
709
|
+
} };
|
|
710
|
+
//#endregion
|
|
647
711
|
//#region src/_hooks/useSelector.ts
|
|
648
712
|
function useSelector(selector, ...params) {
|
|
649
713
|
const props = params[0];
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
714
|
+
const cacheRef = (0, react.useRef)(null);
|
|
715
|
+
let cache = cacheRef.current;
|
|
716
|
+
if (cache === null) {
|
|
717
|
+
const unfrozenProps = unfreezeProps(props);
|
|
718
|
+
cache = cacheRef.current = {
|
|
719
|
+
props,
|
|
720
|
+
unfrozenProps,
|
|
721
|
+
selector,
|
|
722
|
+
boundSelector: (state) => selector(state, unfrozenProps)
|
|
723
|
+
};
|
|
724
|
+
} else {
|
|
725
|
+
const propsChanged = !(0, _veksa_react_redux.shallowEqual)(props, cache.props);
|
|
726
|
+
if (propsChanged) {
|
|
727
|
+
cache.props = props;
|
|
728
|
+
cache.unfrozenProps = unfreezeProps(props);
|
|
729
|
+
}
|
|
730
|
+
if (propsChanged || cache.selector !== selector) {
|
|
731
|
+
const { unfrozenProps } = cache;
|
|
732
|
+
cache.selector = selector;
|
|
733
|
+
cache.boundSelector = (state) => selector(state, unfrozenProps);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
return (0, _veksa_react_redux.useSelector)(cache.boundSelector, selectorOptions);
|
|
737
|
+
}
|
|
738
|
+
//#endregion
|
|
739
|
+
//#region src/_hooks/useSelectors.ts
|
|
740
|
+
const readSelectors = (cache, selectors) => {
|
|
741
|
+
const keys = [];
|
|
742
|
+
const fns = [];
|
|
743
|
+
for (const key in selectors) if (Object.prototype.hasOwnProperty.call(selectors, key)) {
|
|
744
|
+
keys.push(key);
|
|
745
|
+
fns.push(selectors[key]);
|
|
746
|
+
}
|
|
747
|
+
cache.keys = keys;
|
|
748
|
+
cache.selectors = fns;
|
|
749
|
+
cache.results = [];
|
|
750
|
+
cache.value = null;
|
|
751
|
+
};
|
|
752
|
+
/**
|
|
753
|
+
* One pass over the incoming object instead of `shallowEqual`, which would
|
|
754
|
+
* allocate a key array for each side on every render of every call site. Key
|
|
755
|
+
* order is part of the comparison, so a renamed key is caught as well.
|
|
756
|
+
*/
|
|
757
|
+
const hasSameSelectors = (cache, selectors) => {
|
|
758
|
+
const { keys, selectors: fns } = cache;
|
|
759
|
+
let index = 0;
|
|
760
|
+
for (const key in selectors) if (Object.prototype.hasOwnProperty.call(selectors, key)) {
|
|
761
|
+
if (index === keys.length || keys[index] !== key || fns[index] !== selectors[key]) return false;
|
|
762
|
+
index++;
|
|
763
|
+
}
|
|
764
|
+
return index === keys.length;
|
|
765
|
+
};
|
|
766
|
+
/**
|
|
767
|
+
* The structured part: every selector runs, but the result object is rebuilt
|
|
768
|
+
* only when some field actually changed. An unchanged batch keeps its previous
|
|
769
|
+
* reference, so react-redux's default reference equality already bails out —
|
|
770
|
+
* no result allocation and no comparison pass on the steady path.
|
|
771
|
+
*/
|
|
772
|
+
const select = (cache, state) => {
|
|
773
|
+
const { keys, selectors, unfrozenProps, results } = cache;
|
|
774
|
+
const count = keys.length;
|
|
775
|
+
let changed = cache.value === null;
|
|
776
|
+
for (let index = 0; index < count; index++) {
|
|
777
|
+
const result = selectors[index](state, unfrozenProps);
|
|
778
|
+
if (!Object.is(result, results[index])) {
|
|
779
|
+
results[index] = result;
|
|
780
|
+
changed = true;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (!changed) return cache.value;
|
|
784
|
+
const value = {};
|
|
785
|
+
for (let index = 0; index < count; index++) value[keys[index]] = results[index];
|
|
786
|
+
cache.value = value;
|
|
787
|
+
return value;
|
|
788
|
+
};
|
|
789
|
+
function useSelectors(selectors, ...params) {
|
|
790
|
+
const props = params[0];
|
|
791
|
+
const cacheRef = (0, react.useRef)(null);
|
|
792
|
+
let cache = cacheRef.current;
|
|
793
|
+
if (cache === null) {
|
|
794
|
+
const created = {
|
|
795
|
+
keys: [],
|
|
796
|
+
selectors: [],
|
|
797
|
+
props,
|
|
798
|
+
unfrozenProps: unfreezeProps(props),
|
|
799
|
+
results: [],
|
|
800
|
+
value: null,
|
|
801
|
+
boundSelector: (state) => select(created, state)
|
|
802
|
+
};
|
|
803
|
+
readSelectors(created, selectors);
|
|
804
|
+
cache = cacheRef.current = created;
|
|
805
|
+
} else {
|
|
806
|
+
const selectorsChanged = !hasSameSelectors(cache, selectors);
|
|
807
|
+
const propsChanged = !(0, _veksa_react_redux.shallowEqual)(props, cache.props);
|
|
808
|
+
if (selectorsChanged) readSelectors(cache, selectors);
|
|
809
|
+
if (propsChanged) {
|
|
810
|
+
cache.props = props;
|
|
811
|
+
cache.unfrozenProps = unfreezeProps(props);
|
|
812
|
+
}
|
|
813
|
+
if (selectorsChanged || propsChanged) {
|
|
814
|
+
const current = cache;
|
|
815
|
+
cache.boundSelector = (state) => select(current, state);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
return (0, _veksa_react_redux.useSelector)(cache.boundSelector, selectorOptions);
|
|
660
819
|
}
|
|
661
820
|
//#endregion
|
|
662
821
|
exports.IntervalMapCache = IntervalMapCache;
|
|
@@ -673,11 +832,13 @@ exports.createKeySelectorCreator = createKeySelectorCreator;
|
|
|
673
832
|
exports.createPathSelector = createPathSelector;
|
|
674
833
|
exports.createPropSelector = createPropSelector;
|
|
675
834
|
exports.createSegmentSelector = createSegmentSelector;
|
|
835
|
+
exports.createSequenceSelector = createSequenceSelector;
|
|
676
836
|
exports.defaultKeySelector = defaultKeySelector;
|
|
677
837
|
exports.initGarbageCollector = initGarbageCollector;
|
|
678
838
|
exports.isDebugMode = isDebugMode;
|
|
679
839
|
exports.setDebugMode = setDebugMode;
|
|
680
840
|
exports.stringComposeKeySelectors = stringComposeKeySelectors;
|
|
681
841
|
exports.useSelector = useSelector;
|
|
842
|
+
exports.useSelectors = useSelectors;
|
|
682
843
|
|
|
683
844
|
//# sourceMappingURL=reselectKit.development.cjs.map
|