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
package/dist/reselectKit.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { FlatObjectCache, createCachedSelector } from "@veksa/re-reselect";
|
|
2
1
|
import { createSelector, createStructuredSelector } from "@veksa/reselect";
|
|
2
|
+
import { FlatObjectCache, createCachedSelector } from "@veksa/re-reselect";
|
|
3
3
|
import { shallowEqual, useSelector as useSelector$1 } from "@veksa/react-redux";
|
|
4
|
-
import {
|
|
4
|
+
import { useRef } from "react";
|
|
5
5
|
//#region src/createSegmentSelector.ts
|
|
6
6
|
function createSegmentSelector(fn, initial) {
|
|
7
7
|
return (state) => {
|
|
@@ -93,6 +93,15 @@ const innerCreatePathSelector = (baseSelector, path = [], applyMeta = defaultVoi
|
|
|
93
93
|
return innerCreatePathSelector(baseSelector, [...path, String(key)], applyMeta);
|
|
94
94
|
} });
|
|
95
95
|
};
|
|
96
|
+
/**
|
|
97
|
+
* Builds a proxy that turns property access into a selector reading that path.
|
|
98
|
+
*
|
|
99
|
+
* `Params` is inferred straight from the base selector, so the plain and the
|
|
100
|
+
* parametric case share one signature — and one set of builder types — instead
|
|
101
|
+
* of being told apart by a conditional in the parameter position. It defaults
|
|
102
|
+
* to empty so that `createPathSelector<State, Result>(selector)` keeps working
|
|
103
|
+
* for callers that spell the type arguments out.
|
|
104
|
+
*/
|
|
96
105
|
function createPathSelector(baseSelector) {
|
|
97
106
|
return innerCreatePathSelector(baseSelector);
|
|
98
107
|
}
|
|
@@ -259,16 +268,55 @@ const generateBindingName = (binding) => {
|
|
|
259
268
|
}), {});
|
|
260
269
|
return `${Object.keys(structure).join()} -> ${Object.values(structure).join()}`;
|
|
261
270
|
};
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
271
|
+
/**
|
|
272
|
+
* The default binding strategy: applies the binding to the props the caller
|
|
273
|
+
* passed, hands them to the base selector, and puts them back as they were.
|
|
274
|
+
*
|
|
275
|
+
* Almost every binding is a single key — `bound(selector, { positionId })` —
|
|
276
|
+
* and specialising it removes the two allocations the generic path costs per
|
|
277
|
+
* call: the `{ result, rollback }` object and the `rollback` closure. On a
|
|
278
|
+
* graph with a bound selector per row those are the calls that dominate.
|
|
279
|
+
*
|
|
280
|
+
* The `delete` in the restore stays deliberately. Leaving the key behind as
|
|
281
|
+
* `undefined` would be cheaper — it avoids putting the caller's props object
|
|
282
|
+
* into dictionary mode — but it also changes the object's shape, and
|
|
283
|
+
* `useSelector` memoizes props shallowly: a props object that has grown an
|
|
284
|
+
* extra key never again compares equal to a freshly built one, so the
|
|
285
|
+
* memoization the binding exists to protect would be defeated.
|
|
286
|
+
*/
|
|
287
|
+
const applyBinding = (baseSelector, binding) => {
|
|
288
|
+
const source = binding;
|
|
289
|
+
const keys = Object.keys(source);
|
|
290
|
+
if (keys.length === 1) {
|
|
291
|
+
const key = keys[0];
|
|
292
|
+
const value = source[key];
|
|
293
|
+
return (state, props) => {
|
|
294
|
+
const target = props;
|
|
295
|
+
if (typeof target !== "object" || target === null) return baseSelector(state, source);
|
|
296
|
+
const previous = target[key];
|
|
297
|
+
if (previous === value) return baseSelector(state, props);
|
|
298
|
+
const had = Object.prototype.hasOwnProperty.call(target, key);
|
|
299
|
+
target[key] = value;
|
|
300
|
+
try {
|
|
301
|
+
return baseSelector(state, props);
|
|
302
|
+
} finally {
|
|
303
|
+
if (had) target[key] = previous;
|
|
304
|
+
else delete target[key];
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return (state, props) => {
|
|
309
|
+
const { result: nextProps, rollback } = temporaryAssign(props, source);
|
|
310
|
+
try {
|
|
311
|
+
return baseSelector(state, nextProps);
|
|
312
|
+
} finally {
|
|
313
|
+
rollback();
|
|
314
|
+
}
|
|
315
|
+
};
|
|
268
316
|
};
|
|
269
|
-
const
|
|
317
|
+
const createBoundSelector = (baseSelector, binding, options = {}) => {
|
|
270
318
|
var _ref, _options$keySelectorC;
|
|
271
|
-
const bindingStrategy = (_ref = options.bindingStrategy) !== null && _ref !== void 0 ? _ref :
|
|
319
|
+
const bindingStrategy = (_ref = options.bindingStrategy) !== null && _ref !== void 0 ? _ref : applyBinding;
|
|
272
320
|
const keySelectorComposer = (_options$keySelectorC = options.keySelectorComposer) !== null && _options$keySelectorC !== void 0 ? _options$keySelectorC : stringComposeKeySelectors;
|
|
273
321
|
const boundSelector = bindingStrategy(baseSelector, binding);
|
|
274
322
|
Object.assign(boundSelector, baseSelector);
|
|
@@ -295,49 +343,6 @@ const createBoundInnerSelector = (baseSelector, binding, options = {}) => {
|
|
|
295
343
|
}
|
|
296
344
|
return boundSelector;
|
|
297
345
|
};
|
|
298
|
-
const createBoundSelector = (baseSelector, binding) => {
|
|
299
|
-
return createBoundInnerSelector(baseSelector, binding, { bindingStrategy: (selector, bindingProps) => {
|
|
300
|
-
const source = bindingProps;
|
|
301
|
-
const keys = Object.keys(source);
|
|
302
|
-
/**
|
|
303
|
-
* Almost every binding is a single key — `bound(selector, { positionId })` —
|
|
304
|
-
* and specialising it removes the two allocations the generic path costs per
|
|
305
|
-
* call: the `{ result, rollback }` object and the `rollback` closure. On a
|
|
306
|
-
* graph with a bound selector per row those are the calls that dominate.
|
|
307
|
-
*
|
|
308
|
-
* The `delete` in the restore stays deliberately. Leaving the key behind as
|
|
309
|
-
* `undefined` would be cheaper — it avoids putting the caller's props object
|
|
310
|
-
* into dictionary mode — but it also changes the object's shape, and
|
|
311
|
-
* `useSelector` memoizes props shallowly: a props object that has grown an
|
|
312
|
-
* extra key never again compares equal to a freshly built one, so the
|
|
313
|
-
* memoization the binding exists to protect would be defeated.
|
|
314
|
-
*/
|
|
315
|
-
if (keys.length === 1) {
|
|
316
|
-
const key = keys[0];
|
|
317
|
-
const value = source[key];
|
|
318
|
-
return (state, props) => {
|
|
319
|
-
const target = props;
|
|
320
|
-
if (typeof target !== "object" || target === null) return selector(state, source);
|
|
321
|
-
const previous = target[key];
|
|
322
|
-
if (previous === value) return selector(state, props);
|
|
323
|
-
const had = Object.prototype.hasOwnProperty.call(target, key);
|
|
324
|
-
target[key] = value;
|
|
325
|
-
try {
|
|
326
|
-
return selector(state, props);
|
|
327
|
-
} finally {
|
|
328
|
-
if (had) target[key] = previous;
|
|
329
|
-
else delete target[key];
|
|
330
|
-
}
|
|
331
|
-
};
|
|
332
|
-
}
|
|
333
|
-
return (state, props) => {
|
|
334
|
-
const { result: nextProps, rollback } = temporaryAssign(props, source);
|
|
335
|
-
const result = selector(state, nextProps);
|
|
336
|
-
rollback();
|
|
337
|
-
return result;
|
|
338
|
-
};
|
|
339
|
-
} });
|
|
340
|
-
};
|
|
341
346
|
//#endregion
|
|
342
347
|
//#region src/createEmptySelector.ts
|
|
343
348
|
/**
|
|
@@ -366,6 +371,11 @@ const generateMappingName = (mapping) => {
|
|
|
366
371
|
const innerCreateAdaptedSelector = (baseSelector, mapping) => (state, props) => {
|
|
367
372
|
return baseSelector(state, mapping(props));
|
|
368
373
|
};
|
|
374
|
+
/**
|
|
375
|
+
* Rewrites the props a selector reads. Only the props argument changes: the
|
|
376
|
+
* adapted selector takes `[P2]` where the base took `[P1]`, which is the same
|
|
377
|
+
* argument tuple every other creator in the library carries.
|
|
378
|
+
*/
|
|
369
379
|
const createAdaptedSelector = (baseSelector, mapping) => {
|
|
370
380
|
const adaptedSelector = innerCreateAdaptedSelector(baseSelector, mapping);
|
|
371
381
|
Object.assign(adaptedSelector, baseSelector);
|
|
@@ -401,7 +411,8 @@ const generateSelectorKey = (selector) => {
|
|
|
401
411
|
//#endregion
|
|
402
412
|
//#region src/createChainSelector.ts
|
|
403
413
|
function createChainSelector(selector, options, prevChain) {
|
|
404
|
-
const
|
|
414
|
+
const createLevel = (fn, chainOptions, isDerivedKeyless) => {
|
|
415
|
+
var _selectorOptions$sele;
|
|
405
416
|
const combinedOptions = {
|
|
406
417
|
...options,
|
|
407
418
|
...chainOptions
|
|
@@ -409,8 +420,17 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
409
420
|
const keySelector = isCachedSelector(selector) ? selector.keySelector : defaultKeySelector;
|
|
410
421
|
const { createSelectorOptions = () => ({}), keySelectorComposer = stringComposeKeySelectors } = combinedOptions;
|
|
411
422
|
const keySelectorCreator = createKeySelectorCreator(keySelectorComposer);
|
|
412
|
-
|
|
413
|
-
|
|
423
|
+
/**
|
|
424
|
+
* Nothing below this level is keyed, so the cache would hold a single entry
|
|
425
|
+
* for the lifetime of the chain and every call would still pay for the key
|
|
426
|
+
* computation and the lookup that guard it. A plain memoized selector is the
|
|
427
|
+
* same mapping — re-reselect under a constant key is one `createSelector`
|
|
428
|
+
* instance — without the layer.
|
|
429
|
+
*/
|
|
430
|
+
const isKeyless = keySelector === defaultKeySelector;
|
|
431
|
+
const selectorOptions = createSelectorOptions();
|
|
432
|
+
const higherOrderSelector = isKeyless ? ((_selectorOptions$sele = selectorOptions.selectorCreator) !== null && _selectorOptions$sele !== void 0 ? _selectorOptions$sele : createSelector)([selector], fn) : createCachedSelector([selector], fn)({
|
|
433
|
+
...selectorOptions,
|
|
414
434
|
keySelector
|
|
415
435
|
});
|
|
416
436
|
withDebugName(higherOrderSelector, () => {
|
|
@@ -430,37 +450,65 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
430
450
|
return derivedSelector(state, props);
|
|
431
451
|
};
|
|
432
452
|
combinedSelector.dependencies = [higherOrderSelector];
|
|
433
|
-
combinedSelector.cache = higherOrderSelector.cache;
|
|
453
|
+
if (!isKeyless) combinedSelector.cache = higherOrderSelector.cache;
|
|
434
454
|
/**
|
|
435
|
-
*
|
|
436
|
-
*
|
|
437
|
-
*
|
|
438
|
-
*
|
|
439
|
-
* its own key computation, its own cache lookup, and its own evaluation of
|
|
440
|
-
* `higherOrderSelector` — to produce a value that depends on nothing but the
|
|
441
|
-
* derived selector. A `WeakMap` keyed on that selector is the same mapping with
|
|
442
|
-
* none of the layer, and it shares across cache keys rather than per key.
|
|
443
|
-
*
|
|
444
|
-
* Note what is deliberately *not* memoized: the `(state, props)` pair. `props`
|
|
445
|
-
* is mutated in place by `createBoundSelector`, so two calls can pass the same
|
|
446
|
-
* object reference with different contents, and a reference-keyed cache would
|
|
447
|
-
* hand back a stale selector.
|
|
455
|
+
* Assigned through a local and written to the selector once. Assigning the
|
|
456
|
+
* property in both branches instead reads as two expando declarations of
|
|
457
|
+
* `keySelector` to TypeScript before 7, which rejects it as a duplicate
|
|
458
|
+
* identifier.
|
|
448
459
|
*/
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
};
|
|
460
|
+
let levelKeySelector;
|
|
461
|
+
if (isDerivedKeyless)
|
|
462
|
+
/**
|
|
463
|
+
* A `map` level derives a plain `() => output`, never a cached selector, so
|
|
464
|
+
* it contributes nothing to the composed key and the whole composition is
|
|
465
|
+
* known at build time. It is the same call the wrapper below makes, minus
|
|
466
|
+
* the derived selector — so the key is composed exactly as before, once,
|
|
467
|
+
* instead of on every key computation of every level above.
|
|
468
|
+
*/
|
|
469
|
+
levelKeySelector = keySelectorCreator({ inputSelectors: [higherOrderSelector] });
|
|
470
|
+
else {
|
|
471
|
+
/**
|
|
472
|
+
* The derived selector's key selector, memoized on the derived selector itself.
|
|
473
|
+
*
|
|
474
|
+
* This used to be a second `createCachedSelector` over `[higherOrderSelector]`,
|
|
475
|
+
* which meant every key computation paid a whole extra cached-selector layer —
|
|
476
|
+
* its own key computation, its own cache lookup, and its own evaluation of
|
|
477
|
+
* `higherOrderSelector` — to produce a value that depends on nothing but the
|
|
478
|
+
* derived selector. A `WeakMap` keyed on that selector is the same mapping with
|
|
479
|
+
* none of the layer, and it shares across cache keys rather than per key.
|
|
480
|
+
*
|
|
481
|
+
* Note what is deliberately *not* memoized: the `(state, props)` pair. `props`
|
|
482
|
+
* is mutated in place by `createBoundSelector`, so two calls can pass the same
|
|
483
|
+
* object reference with different contents, and a reference-keyed cache would
|
|
484
|
+
* hand back a stale selector.
|
|
485
|
+
*/
|
|
486
|
+
const derivedKeySelectors = /* @__PURE__ */ new WeakMap();
|
|
487
|
+
levelKeySelector = (state, props) => {
|
|
488
|
+
const derivedSelector = higherOrderSelector(state, props);
|
|
489
|
+
let derivedKeySelector = derivedKeySelectors.get(derivedSelector);
|
|
490
|
+
if (derivedKeySelector === void 0) {
|
|
491
|
+
derivedKeySelector = keySelectorCreator({ inputSelectors: [higherOrderSelector, derivedSelector] });
|
|
492
|
+
derivedKeySelectors.set(derivedSelector, derivedKeySelector);
|
|
493
|
+
}
|
|
494
|
+
return derivedKeySelector(state, props);
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
/**
|
|
498
|
+
* When nothing keyed is left the composition yields the default key, and
|
|
499
|
+
* leaving the property off is what lets the next level skip its cache too.
|
|
500
|
+
* The wrapper above is a fresh closure, so only a `map` level can land here
|
|
501
|
+
* with the default.
|
|
502
|
+
*/
|
|
503
|
+
if (levelKeySelector !== defaultKeySelector) combinedSelector.keySelector = levelKeySelector;
|
|
459
504
|
withDebugName(combinedSelector, () => {
|
|
460
505
|
return `${getSelectorName(selector)} (will be chained ${stringifyFunction(fn)})`;
|
|
461
506
|
});
|
|
462
507
|
return createChainSelector(combinedSelector, combinedOptions, Object.assign(fn, { parentChain: prevChain }));
|
|
463
508
|
};
|
|
509
|
+
const chain = (fn, chainOptions) => {
|
|
510
|
+
return createLevel(fn, chainOptions, false);
|
|
511
|
+
};
|
|
464
512
|
const map = (fn, mapOptions) => {
|
|
465
513
|
const mapSelector = (result) => {
|
|
466
514
|
const output = fn(result);
|
|
@@ -470,7 +518,7 @@ function createChainSelector(selector, options, prevChain) {
|
|
|
470
518
|
});
|
|
471
519
|
return mapSelector;
|
|
472
520
|
};
|
|
473
|
-
return
|
|
521
|
+
return createLevel(mapSelector, mapOptions, true);
|
|
474
522
|
};
|
|
475
523
|
const build = () => {
|
|
476
524
|
return Object.assign(selector, { chainHierarchy: prevChain });
|
|
@@ -651,28 +699,140 @@ var TreeCache = class {
|
|
|
651
699
|
}
|
|
652
700
|
};
|
|
653
701
|
//#endregion
|
|
654
|
-
//#region src/
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
702
|
+
//#region src/_helpers/unfreezeProps.ts
|
|
703
|
+
/**
|
|
704
|
+
* Bound selectors write the binding into the props object in place and roll it
|
|
705
|
+
* back afterwards (see temporaryAssign.ts), which throws on a frozen target in
|
|
706
|
+
* strict mode. React freezes element props in development, and immer-based
|
|
707
|
+
* stores hand out frozen objects in production too, so the copy stays in both
|
|
708
|
+
* builds — the hooks only ever call this when props actually change.
|
|
709
|
+
*/
|
|
710
|
+
const unfreezeProps = (props) => {
|
|
711
|
+
if (typeof props !== "object" || props === null || !Object.isFrozen(props)) return props;
|
|
712
|
+
return Array.isArray(props) ? props.slice() : { ...props };
|
|
659
713
|
};
|
|
660
714
|
//#endregion
|
|
715
|
+
//#region src/_hooks/selectorOptions.ts
|
|
716
|
+
/**
|
|
717
|
+
* Hoisted: react-redux reads the options object on every render, so there is no
|
|
718
|
+
* reason to allocate a fresh one per call site per render. The dev-mode checks
|
|
719
|
+
* re-run the selector to compare results, which these hooks control themselves.
|
|
720
|
+
*/
|
|
721
|
+
const selectorOptions = { devModeChecks: {
|
|
722
|
+
stabilityCheck: "never",
|
|
723
|
+
identityFunctionCheck: "never"
|
|
724
|
+
} };
|
|
725
|
+
//#endregion
|
|
661
726
|
//#region src/_hooks/useSelector.ts
|
|
662
727
|
function useSelector(selector, ...params) {
|
|
663
728
|
const props = params[0];
|
|
664
|
-
const
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
729
|
+
const cacheRef = useRef(null);
|
|
730
|
+
let cache = cacheRef.current;
|
|
731
|
+
if (cache === null) {
|
|
732
|
+
const unfrozenProps = unfreezeProps(props);
|
|
733
|
+
cache = cacheRef.current = {
|
|
734
|
+
props,
|
|
735
|
+
unfrozenProps,
|
|
736
|
+
selector,
|
|
737
|
+
boundSelector: (state) => selector(state, unfrozenProps)
|
|
738
|
+
};
|
|
739
|
+
} else {
|
|
740
|
+
const propsChanged = !shallowEqual(props, cache.props);
|
|
741
|
+
if (propsChanged) {
|
|
742
|
+
cache.props = props;
|
|
743
|
+
cache.unfrozenProps = unfreezeProps(props);
|
|
744
|
+
}
|
|
745
|
+
if (propsChanged || cache.selector !== selector) {
|
|
746
|
+
const { unfrozenProps } = cache;
|
|
747
|
+
cache.selector = selector;
|
|
748
|
+
cache.boundSelector = (state) => selector(state, unfrozenProps);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
return useSelector$1(cache.boundSelector, selectorOptions);
|
|
752
|
+
}
|
|
753
|
+
//#endregion
|
|
754
|
+
//#region src/_hooks/useSelectors.ts
|
|
755
|
+
const readSelectors = (cache, selectors) => {
|
|
756
|
+
const keys = [];
|
|
757
|
+
const fns = [];
|
|
758
|
+
for (const key in selectors) if (Object.prototype.hasOwnProperty.call(selectors, key)) {
|
|
759
|
+
keys.push(key);
|
|
760
|
+
fns.push(selectors[key]);
|
|
761
|
+
}
|
|
762
|
+
cache.keys = keys;
|
|
763
|
+
cache.selectors = fns;
|
|
764
|
+
cache.results = [];
|
|
765
|
+
cache.value = null;
|
|
766
|
+
};
|
|
767
|
+
/**
|
|
768
|
+
* One pass over the incoming object instead of `shallowEqual`, which would
|
|
769
|
+
* allocate a key array for each side on every render of every call site. Key
|
|
770
|
+
* order is part of the comparison, so a renamed key is caught as well.
|
|
771
|
+
*/
|
|
772
|
+
const hasSameSelectors = (cache, selectors) => {
|
|
773
|
+
const { keys, selectors: fns } = cache;
|
|
774
|
+
let index = 0;
|
|
775
|
+
for (const key in selectors) if (Object.prototype.hasOwnProperty.call(selectors, key)) {
|
|
776
|
+
if (index === keys.length || keys[index] !== key || fns[index] !== selectors[key]) return false;
|
|
777
|
+
index++;
|
|
778
|
+
}
|
|
779
|
+
return index === keys.length;
|
|
780
|
+
};
|
|
781
|
+
/**
|
|
782
|
+
* The structured part: every selector runs, but the result object is rebuilt
|
|
783
|
+
* only when some field actually changed. An unchanged batch keeps its previous
|
|
784
|
+
* reference, so react-redux's default reference equality already bails out —
|
|
785
|
+
* no result allocation and no comparison pass on the steady path.
|
|
786
|
+
*/
|
|
787
|
+
const select = (cache, state) => {
|
|
788
|
+
const { keys, selectors, unfrozenProps, results } = cache;
|
|
789
|
+
const count = keys.length;
|
|
790
|
+
let changed = cache.value === null;
|
|
791
|
+
for (let index = 0; index < count; index++) {
|
|
792
|
+
const result = selectors[index](state, unfrozenProps);
|
|
793
|
+
if (!Object.is(result, results[index])) {
|
|
794
|
+
results[index] = result;
|
|
795
|
+
changed = true;
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
if (!changed) return cache.value;
|
|
799
|
+
const value = {};
|
|
800
|
+
for (let index = 0; index < count; index++) value[keys[index]] = results[index];
|
|
801
|
+
cache.value = value;
|
|
802
|
+
return value;
|
|
803
|
+
};
|
|
804
|
+
function useSelectors(selectors, ...params) {
|
|
805
|
+
const props = params[0];
|
|
806
|
+
const cacheRef = useRef(null);
|
|
807
|
+
let cache = cacheRef.current;
|
|
808
|
+
if (cache === null) {
|
|
809
|
+
const created = {
|
|
810
|
+
keys: [],
|
|
811
|
+
selectors: [],
|
|
812
|
+
props,
|
|
813
|
+
unfrozenProps: unfreezeProps(props),
|
|
814
|
+
results: [],
|
|
815
|
+
value: null,
|
|
816
|
+
boundSelector: (state) => select(created, state)
|
|
817
|
+
};
|
|
818
|
+
readSelectors(created, selectors);
|
|
819
|
+
cache = cacheRef.current = created;
|
|
820
|
+
} else {
|
|
821
|
+
const selectorsChanged = !hasSameSelectors(cache, selectors);
|
|
822
|
+
const propsChanged = !shallowEqual(props, cache.props);
|
|
823
|
+
if (selectorsChanged) readSelectors(cache, selectors);
|
|
824
|
+
if (propsChanged) {
|
|
825
|
+
cache.props = props;
|
|
826
|
+
cache.unfrozenProps = unfreezeProps(props);
|
|
827
|
+
}
|
|
828
|
+
if (selectorsChanged || propsChanged) {
|
|
829
|
+
const current = cache;
|
|
830
|
+
cache.boundSelector = (state) => select(current, state);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
return useSelector$1(cache.boundSelector, selectorOptions);
|
|
674
834
|
}
|
|
675
835
|
//#endregion
|
|
676
|
-
export { IntervalMapCache, TreeCache, arrayComposeKeySelectors, createAdaptedSelector, createBoundSelector, createCachedSequenceSelector, createCachedStructuredSelector, createChainSelector, createEmptySelector, createKeySelectorComposer, createKeySelectorCreator, createPathSelector, createPropSelector, createSegmentSelector, defaultKeySelector, initGarbageCollector, isDebugMode, setDebugMode, stringComposeKeySelectors, useSelector };
|
|
836
|
+
export { IntervalMapCache, TreeCache, arrayComposeKeySelectors, createAdaptedSelector, createBoundSelector, createCachedSequenceSelector, createCachedStructuredSelector, createChainSelector, createEmptySelector, createKeySelectorComposer, createKeySelectorCreator, createPathSelector, createPropSelector, createSegmentSelector, createSequenceSelector, defaultKeySelector, initGarbageCollector, isDebugMode, setDebugMode, stringComposeKeySelectors, useSelector, useSelectors };
|
|
677
837
|
|
|
678
838
|
//# sourceMappingURL=reselectKit.mjs.map
|