mobx 4.12.0 → 4.14.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/lib/api/autorun.d.ts +5 -0
- package/lib/api/configure.d.ts +10 -0
- package/lib/api/when.d.ts +5 -0
- package/lib/core/action.d.ts +13 -0
- package/lib/core/derivation.d.ts +7 -0
- package/lib/core/globalstate.d.ts +15 -0
- package/lib/core/reaction.d.ts +2 -1
- package/lib/mobx.d.ts +1 -1
- package/lib/mobx.es6.js +499 -394
- package/lib/mobx.js +498 -391
- package/lib/mobx.js.flow +20 -2
- package/lib/mobx.min.js +1 -1
- package/lib/mobx.module.js +498 -393
- package/lib/mobx.umd.js +498 -391
- package/lib/mobx.umd.min.js +1 -1
- package/lib/utils/comparer.d.ts +2 -0
- package/lib/utils/eq.d.ts +1 -1
- package/lib/utils/utils.d.ts +1 -0
- package/package.json +1 -1
package/lib/mobx.es6.js
CHANGED
|
@@ -4,8 +4,15 @@ const EMPTY_ARRAY = [];
|
|
|
4
4
|
Object.freeze(EMPTY_ARRAY);
|
|
5
5
|
const EMPTY_OBJECT = {};
|
|
6
6
|
Object.freeze(EMPTY_OBJECT);
|
|
7
|
+
const mockGlobal = {};
|
|
7
8
|
function getGlobal() {
|
|
8
|
-
|
|
9
|
+
if (typeof window !== "undefined") {
|
|
10
|
+
return window;
|
|
11
|
+
}
|
|
12
|
+
if (typeof global !== "undefined") {
|
|
13
|
+
return global;
|
|
14
|
+
}
|
|
15
|
+
return mockGlobal;
|
|
9
16
|
}
|
|
10
17
|
function getNextId() {
|
|
11
18
|
return ++globalState.mobxGuid;
|
|
@@ -65,6 +72,20 @@ function isPlainObject(value) {
|
|
|
65
72
|
const proto = Object.getPrototypeOf(value);
|
|
66
73
|
return proto === Object.prototype || proto === null;
|
|
67
74
|
}
|
|
75
|
+
function convertToMap(dataStructure) {
|
|
76
|
+
if (isES6Map(dataStructure) || isObservableMap(dataStructure)) {
|
|
77
|
+
return dataStructure;
|
|
78
|
+
}
|
|
79
|
+
else if (Array.isArray(dataStructure)) {
|
|
80
|
+
return new Map(dataStructure);
|
|
81
|
+
}
|
|
82
|
+
else if (isPlainObject(dataStructure)) {
|
|
83
|
+
return new Map(Object.entries(dataStructure));
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
return fail(`Cannot convert to map from '${dataStructure}'`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
68
89
|
function makeNonEnumerable(object, propNames) {
|
|
69
90
|
for (let i = 0; i < propNames.length; i++) {
|
|
70
91
|
addHiddenProp(object, propNames[i], object[propNames[i]]);
|
|
@@ -118,15 +139,6 @@ function isES6Map(thing) {
|
|
|
118
139
|
function isES6Set(thing) {
|
|
119
140
|
return thing instanceof Set;
|
|
120
141
|
}
|
|
121
|
-
function getMapLikeKeys(map) {
|
|
122
|
-
if (isPlainObject(map))
|
|
123
|
-
return Object.keys(map);
|
|
124
|
-
if (Array.isArray(map))
|
|
125
|
-
return map.map(([key]) => key);
|
|
126
|
-
if (isES6Map(map) || isObservableMap(map))
|
|
127
|
-
return iteratorToArray(map.keys());
|
|
128
|
-
return fail(`Cannot get keys from '${map}'`);
|
|
129
|
-
}
|
|
130
142
|
// use Array.from in Mobx 5
|
|
131
143
|
function iteratorToArray(it) {
|
|
132
144
|
const res = [];
|
|
@@ -153,13 +165,13 @@ function declareIterator(prototType, iteratorFactory) {
|
|
|
153
165
|
addHiddenFinalProp(prototType, iteratorSymbol(), iteratorFactory);
|
|
154
166
|
}
|
|
155
167
|
function makeIterable(iterator) {
|
|
156
|
-
iterator[iteratorSymbol()] =
|
|
168
|
+
iterator[iteratorSymbol()] = getSelf;
|
|
157
169
|
return iterator;
|
|
158
170
|
}
|
|
159
171
|
function toStringTagSymbol() {
|
|
160
172
|
return (typeof Symbol === "function" && Symbol.toStringTag) || "@@toStringTag";
|
|
161
173
|
}
|
|
162
|
-
function
|
|
174
|
+
function getSelf() {
|
|
163
175
|
return this;
|
|
164
176
|
}
|
|
165
177
|
|
|
@@ -223,13 +235,17 @@ function identityComparer(a, b) {
|
|
|
223
235
|
function structuralComparer(a, b) {
|
|
224
236
|
return deepEqual(a, b);
|
|
225
237
|
}
|
|
238
|
+
function shallowComparer(a, b) {
|
|
239
|
+
return deepEqual(a, b, 1);
|
|
240
|
+
}
|
|
226
241
|
function defaultComparer(a, b) {
|
|
227
242
|
return areBothNaN(a, b) || identityComparer(a, b);
|
|
228
243
|
}
|
|
229
244
|
const comparer = {
|
|
230
245
|
identity: identityComparer,
|
|
231
246
|
structural: structuralComparer,
|
|
232
|
-
default: defaultComparer
|
|
247
|
+
default: defaultComparer,
|
|
248
|
+
shallow: shallowComparer
|
|
233
249
|
};
|
|
234
250
|
|
|
235
251
|
const enumerableDescriptorCache = {};
|
|
@@ -549,6 +565,275 @@ const computed = function computed(arg1, arg2, arg3) {
|
|
|
549
565
|
};
|
|
550
566
|
computed.struct = computedStructDecorator;
|
|
551
567
|
|
|
568
|
+
var IDerivationState;
|
|
569
|
+
(function (IDerivationState) {
|
|
570
|
+
// before being run or (outside batch and not being observed)
|
|
571
|
+
// at this point derivation is not holding any data about dependency tree
|
|
572
|
+
IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
|
|
573
|
+
// no shallow dependency changed since last computation
|
|
574
|
+
// won't recalculate derivation
|
|
575
|
+
// this is what makes mobx fast
|
|
576
|
+
IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
|
|
577
|
+
// some deep dependency changed, but don't know if shallow dependency changed
|
|
578
|
+
// will require to check first if UP_TO_DATE or POSSIBLY_STALE
|
|
579
|
+
// currently only ComputedValue will propagate POSSIBLY_STALE
|
|
580
|
+
//
|
|
581
|
+
// having this state is second big optimization:
|
|
582
|
+
// don't have to recompute on every dependency change, but only when it's needed
|
|
583
|
+
IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
|
|
584
|
+
// A shallow dependency has changed since last computation and the derivation
|
|
585
|
+
// will need to recompute when it's needed next.
|
|
586
|
+
IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
|
|
587
|
+
})(IDerivationState || (IDerivationState = {}));
|
|
588
|
+
var TraceMode;
|
|
589
|
+
(function (TraceMode) {
|
|
590
|
+
TraceMode[TraceMode["NONE"] = 0] = "NONE";
|
|
591
|
+
TraceMode[TraceMode["LOG"] = 1] = "LOG";
|
|
592
|
+
TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
|
|
593
|
+
})(TraceMode || (TraceMode = {}));
|
|
594
|
+
class CaughtException {
|
|
595
|
+
constructor(cause) {
|
|
596
|
+
this.cause = cause;
|
|
597
|
+
// Empty
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
function isCaughtException(e) {
|
|
601
|
+
return e instanceof CaughtException;
|
|
602
|
+
}
|
|
603
|
+
/**
|
|
604
|
+
* Finds out whether any dependency of the derivation has actually changed.
|
|
605
|
+
* If dependenciesState is 1 then it will recalculate dependencies,
|
|
606
|
+
* if any dependency changed it will propagate it by changing dependenciesState to 2.
|
|
607
|
+
*
|
|
608
|
+
* By iterating over the dependencies in the same order that they were reported and
|
|
609
|
+
* stopping on the first change, all the recalculations are only called for ComputedValues
|
|
610
|
+
* that will be tracked by derivation. That is because we assume that if the first x
|
|
611
|
+
* dependencies of the derivation doesn't change then the derivation should run the same way
|
|
612
|
+
* up until accessing x-th dependency.
|
|
613
|
+
*/
|
|
614
|
+
function shouldCompute(derivation) {
|
|
615
|
+
switch (derivation.dependenciesState) {
|
|
616
|
+
case IDerivationState.UP_TO_DATE:
|
|
617
|
+
return false;
|
|
618
|
+
case IDerivationState.NOT_TRACKING:
|
|
619
|
+
case IDerivationState.STALE:
|
|
620
|
+
return true;
|
|
621
|
+
case IDerivationState.POSSIBLY_STALE: {
|
|
622
|
+
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
|
|
623
|
+
const obs = derivation.observing, l = obs.length;
|
|
624
|
+
for (let i = 0; i < l; i++) {
|
|
625
|
+
const obj = obs[i];
|
|
626
|
+
if (isComputedValue(obj)) {
|
|
627
|
+
if (globalState.disableErrorBoundaries) {
|
|
628
|
+
obj.get();
|
|
629
|
+
}
|
|
630
|
+
else {
|
|
631
|
+
try {
|
|
632
|
+
obj.get();
|
|
633
|
+
}
|
|
634
|
+
catch (e) {
|
|
635
|
+
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
|
|
636
|
+
untrackedEnd(prevUntracked);
|
|
637
|
+
return true;
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
|
|
641
|
+
// and `derivation` is an observer of `obj`
|
|
642
|
+
// invariantShouldCompute(derivation)
|
|
643
|
+
if (derivation.dependenciesState === IDerivationState.STALE) {
|
|
644
|
+
untrackedEnd(prevUntracked);
|
|
645
|
+
return true;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
changeDependenciesStateTo0(derivation);
|
|
650
|
+
untrackedEnd(prevUntracked);
|
|
651
|
+
return false;
|
|
652
|
+
}
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// function invariantShouldCompute(derivation: IDerivation) {
|
|
656
|
+
// const newDepState = (derivation as any).dependenciesState
|
|
657
|
+
// if (
|
|
658
|
+
// process.env.NODE_ENV === "production" &&
|
|
659
|
+
// (newDepState === IDerivationState.POSSIBLY_STALE ||
|
|
660
|
+
// newDepState === IDerivationState.NOT_TRACKING)
|
|
661
|
+
// )
|
|
662
|
+
// fail("Illegal dependency state")
|
|
663
|
+
// }
|
|
664
|
+
function isComputingDerivation() {
|
|
665
|
+
return globalState.trackingDerivation !== null; // filter out actions inside computations
|
|
666
|
+
}
|
|
667
|
+
function checkIfStateModificationsAreAllowed(atom) {
|
|
668
|
+
const hasObservers = atom.observers.length > 0;
|
|
669
|
+
// Should never be possible to change an observed observable from inside computed, see #798
|
|
670
|
+
if (globalState.computationDepth > 0 && hasObservers)
|
|
671
|
+
fail(process.env.NODE_ENV !== "production" &&
|
|
672
|
+
`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
|
|
673
|
+
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
|
|
674
|
+
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
|
|
675
|
+
fail(process.env.NODE_ENV !== "production" &&
|
|
676
|
+
(globalState.enforceActions
|
|
677
|
+
? "Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: "
|
|
678
|
+
: "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, the render function of a React component? Tried to modify: ") +
|
|
679
|
+
atom.name);
|
|
680
|
+
}
|
|
681
|
+
function checkIfStateReadsAreAllowed(observable) {
|
|
682
|
+
if (process.env.NODE_ENV !== "production" &&
|
|
683
|
+
!globalState.allowStateReads &&
|
|
684
|
+
globalState.observableRequiresReaction) {
|
|
685
|
+
console.warn(`[mobx] Observable ${observable.name} being read outside a reactive context`);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
/**
|
|
689
|
+
* Executes the provided function `f` and tracks which observables are being accessed.
|
|
690
|
+
* The tracking information is stored on the `derivation` object and the derivation is registered
|
|
691
|
+
* as observer of any of the accessed observables.
|
|
692
|
+
*/
|
|
693
|
+
function trackDerivedFunction(derivation, f, context) {
|
|
694
|
+
const prevAllowStateReads = allowStateReadsStart(true);
|
|
695
|
+
// pre allocate array allocation + room for variation in deps
|
|
696
|
+
// array will be trimmed by bindDependencies
|
|
697
|
+
changeDependenciesStateTo0(derivation);
|
|
698
|
+
derivation.newObserving = new Array(derivation.observing.length + 100);
|
|
699
|
+
derivation.unboundDepsCount = 0;
|
|
700
|
+
derivation.runId = ++globalState.runId;
|
|
701
|
+
const prevTracking = globalState.trackingDerivation;
|
|
702
|
+
globalState.trackingDerivation = derivation;
|
|
703
|
+
let result;
|
|
704
|
+
if (globalState.disableErrorBoundaries === true) {
|
|
705
|
+
result = f.call(context);
|
|
706
|
+
}
|
|
707
|
+
else {
|
|
708
|
+
try {
|
|
709
|
+
result = f.call(context);
|
|
710
|
+
}
|
|
711
|
+
catch (e) {
|
|
712
|
+
result = new CaughtException(e);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
globalState.trackingDerivation = prevTracking;
|
|
716
|
+
bindDependencies(derivation);
|
|
717
|
+
if (derivation.observing.length === 0) {
|
|
718
|
+
warnAboutDerivationWithoutDependencies(derivation);
|
|
719
|
+
}
|
|
720
|
+
allowStateReadsEnd(prevAllowStateReads);
|
|
721
|
+
return result;
|
|
722
|
+
}
|
|
723
|
+
function warnAboutDerivationWithoutDependencies(derivation) {
|
|
724
|
+
if (process.env.NODE_ENV === "production")
|
|
725
|
+
return;
|
|
726
|
+
if (globalState.reactionRequiresObservable || derivation.requiresObservable) {
|
|
727
|
+
console.warn(`[mobx] Derivation ${derivation.name} is created/updated without reading any observable value`);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
/**
|
|
731
|
+
* diffs newObserving with observing.
|
|
732
|
+
* update observing to be newObserving with unique observables
|
|
733
|
+
* notify observers that become observed/unobserved
|
|
734
|
+
*/
|
|
735
|
+
function bindDependencies(derivation) {
|
|
736
|
+
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
|
|
737
|
+
const prevObserving = derivation.observing;
|
|
738
|
+
const observing = (derivation.observing = derivation.newObserving);
|
|
739
|
+
let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
|
|
740
|
+
// Go through all new observables and check diffValue: (this list can contain duplicates):
|
|
741
|
+
// 0: first occurrence, change to 1 and keep it
|
|
742
|
+
// 1: extra occurrence, drop it
|
|
743
|
+
let i0 = 0, l = derivation.unboundDepsCount;
|
|
744
|
+
for (let i = 0; i < l; i++) {
|
|
745
|
+
const dep = observing[i];
|
|
746
|
+
if (dep.diffValue === 0) {
|
|
747
|
+
dep.diffValue = 1;
|
|
748
|
+
if (i0 !== i)
|
|
749
|
+
observing[i0] = dep;
|
|
750
|
+
i0++;
|
|
751
|
+
}
|
|
752
|
+
// Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
|
|
753
|
+
// not hitting the condition
|
|
754
|
+
if (dep.dependenciesState > lowestNewObservingDerivationState) {
|
|
755
|
+
lowestNewObservingDerivationState = dep.dependenciesState;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
observing.length = i0;
|
|
759
|
+
derivation.newObserving = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
|
|
760
|
+
// Go through all old observables and check diffValue: (it is unique after last bindDependencies)
|
|
761
|
+
// 0: it's not in new observables, unobserve it
|
|
762
|
+
// 1: it keeps being observed, don't want to notify it. change to 0
|
|
763
|
+
l = prevObserving.length;
|
|
764
|
+
while (l--) {
|
|
765
|
+
const dep = prevObserving[l];
|
|
766
|
+
if (dep.diffValue === 0) {
|
|
767
|
+
removeObserver(dep, derivation);
|
|
768
|
+
}
|
|
769
|
+
dep.diffValue = 0;
|
|
770
|
+
}
|
|
771
|
+
// Go through all new observables and check diffValue: (now it should be unique)
|
|
772
|
+
// 0: it was set to 0 in last loop. don't need to do anything.
|
|
773
|
+
// 1: it wasn't observed, let's observe it. set back to 0
|
|
774
|
+
while (i0--) {
|
|
775
|
+
const dep = observing[i0];
|
|
776
|
+
if (dep.diffValue === 1) {
|
|
777
|
+
dep.diffValue = 0;
|
|
778
|
+
addObserver(dep, derivation);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
// Some new observed derivations may become stale during this derivation computation
|
|
782
|
+
// so they have had no chance to propagate staleness (#916)
|
|
783
|
+
if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
|
|
784
|
+
derivation.dependenciesState = lowestNewObservingDerivationState;
|
|
785
|
+
derivation.onBecomeStale();
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
function clearObserving(derivation) {
|
|
789
|
+
// invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
|
|
790
|
+
const obs = derivation.observing;
|
|
791
|
+
derivation.observing = [];
|
|
792
|
+
let i = obs.length;
|
|
793
|
+
while (i--)
|
|
794
|
+
removeObserver(obs[i], derivation);
|
|
795
|
+
derivation.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
796
|
+
}
|
|
797
|
+
function untracked(action) {
|
|
798
|
+
const prev = untrackedStart();
|
|
799
|
+
const res = action();
|
|
800
|
+
untrackedEnd(prev);
|
|
801
|
+
return res;
|
|
802
|
+
}
|
|
803
|
+
function untrackedStart() {
|
|
804
|
+
const prev = globalState.trackingDerivation;
|
|
805
|
+
globalState.trackingDerivation = null;
|
|
806
|
+
return prev;
|
|
807
|
+
}
|
|
808
|
+
function untrackedEnd(prev) {
|
|
809
|
+
globalState.trackingDerivation = prev;
|
|
810
|
+
}
|
|
811
|
+
function allowStateReadsStart(allowStateReads) {
|
|
812
|
+
const prev = globalState.allowStateReads;
|
|
813
|
+
globalState.allowStateReads = allowStateReads;
|
|
814
|
+
return prev;
|
|
815
|
+
}
|
|
816
|
+
function allowStateReadsEnd(prev) {
|
|
817
|
+
globalState.allowStateReads = prev;
|
|
818
|
+
}
|
|
819
|
+
/**
|
|
820
|
+
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
|
|
821
|
+
*
|
|
822
|
+
*/
|
|
823
|
+
function changeDependenciesStateTo0(derivation) {
|
|
824
|
+
if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
|
|
825
|
+
return;
|
|
826
|
+
derivation.dependenciesState = IDerivationState.UP_TO_DATE;
|
|
827
|
+
const obs = derivation.observing;
|
|
828
|
+
let i = obs.length;
|
|
829
|
+
while (i--)
|
|
830
|
+
obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// we don't use globalState for these in order to avoid possible issues with multiple
|
|
834
|
+
// mobx versions
|
|
835
|
+
let currentActionId = 0;
|
|
836
|
+
let nextActionId = 1;
|
|
552
837
|
function createAction(actionName, fn) {
|
|
553
838
|
if (process.env.NODE_ENV !== "production") {
|
|
554
839
|
invariant(typeof fn === "function", "`action` can only be invoked on functions");
|
|
@@ -562,25 +847,19 @@ function createAction(actionName, fn) {
|
|
|
562
847
|
return res;
|
|
563
848
|
}
|
|
564
849
|
function executeAction(actionName, fn, scope, args) {
|
|
565
|
-
const runInfo =
|
|
566
|
-
let shouldSupressReactionError = true;
|
|
850
|
+
const runInfo = _startAction(actionName, scope, args);
|
|
567
851
|
try {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
852
|
+
return fn.apply(scope, args);
|
|
853
|
+
}
|
|
854
|
+
catch (err) {
|
|
855
|
+
runInfo.error = err;
|
|
856
|
+
throw err;
|
|
571
857
|
}
|
|
572
858
|
finally {
|
|
573
|
-
|
|
574
|
-
globalState.suppressReactionErrors = shouldSupressReactionError;
|
|
575
|
-
endAction(runInfo);
|
|
576
|
-
globalState.suppressReactionErrors = false;
|
|
577
|
-
}
|
|
578
|
-
else {
|
|
579
|
-
endAction(runInfo);
|
|
580
|
-
}
|
|
859
|
+
_endAction(runInfo);
|
|
581
860
|
}
|
|
582
861
|
}
|
|
583
|
-
function
|
|
862
|
+
function _startAction(actionName, scope, args) {
|
|
584
863
|
const notifySpy = isSpyEnabled() && !!actionName;
|
|
585
864
|
let startTime = 0;
|
|
586
865
|
if (notifySpy) {
|
|
@@ -600,19 +879,35 @@ function startAction(actionName, fn, scope, args) {
|
|
|
600
879
|
const prevDerivation = untrackedStart();
|
|
601
880
|
startBatch();
|
|
602
881
|
const prevAllowStateChanges = allowStateChangesStart(true);
|
|
603
|
-
|
|
882
|
+
const prevAllowStateReads = allowStateReadsStart(true);
|
|
883
|
+
const runInfo = {
|
|
604
884
|
prevDerivation,
|
|
605
885
|
prevAllowStateChanges,
|
|
886
|
+
prevAllowStateReads,
|
|
606
887
|
notifySpy,
|
|
607
|
-
startTime
|
|
888
|
+
startTime,
|
|
889
|
+
actionId: nextActionId++,
|
|
890
|
+
parentActionId: currentActionId
|
|
608
891
|
};
|
|
892
|
+
currentActionId = runInfo.actionId;
|
|
893
|
+
return runInfo;
|
|
609
894
|
}
|
|
610
|
-
function
|
|
895
|
+
function _endAction(runInfo) {
|
|
896
|
+
if (currentActionId !== runInfo.actionId) {
|
|
897
|
+
fail("invalid action stack. did you forget to finish an action?");
|
|
898
|
+
}
|
|
899
|
+
currentActionId = runInfo.parentActionId;
|
|
900
|
+
if (runInfo.error !== undefined) {
|
|
901
|
+
globalState.suppressReactionErrors = true;
|
|
902
|
+
}
|
|
611
903
|
allowStateChangesEnd(runInfo.prevAllowStateChanges);
|
|
904
|
+
allowStateReadsEnd(runInfo.prevAllowStateReads);
|
|
612
905
|
endBatch();
|
|
613
906
|
untrackedEnd(runInfo.prevDerivation);
|
|
614
|
-
if (runInfo.notifySpy)
|
|
907
|
+
if (runInfo.notifySpy) {
|
|
615
908
|
spyReportEnd({ time: Date.now() - runInfo.startTime });
|
|
909
|
+
}
|
|
910
|
+
globalState.suppressReactionErrors = false;
|
|
616
911
|
}
|
|
617
912
|
function allowStateChanges(allowStateChanges, func) {
|
|
618
913
|
const prev = allowStateChangesStart(allowStateChanges);
|
|
@@ -847,350 +1142,112 @@ class ComputedValue {
|
|
|
847
1142
|
if (this.setter) {
|
|
848
1143
|
invariant(!this.isRunningSetter, `The setter of computed value '${this.name}' is trying to update itself. Did you intend to update an _observable_ value, instead of the computed property?`);
|
|
849
1144
|
this.isRunningSetter = true;
|
|
850
|
-
try {
|
|
851
|
-
this.setter.call(this.scope, value);
|
|
852
|
-
}
|
|
853
|
-
finally {
|
|
854
|
-
this.isRunningSetter = false;
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else
|
|
858
|
-
invariant(false, process.env.NODE_ENV !== "production" &&
|
|
859
|
-
`[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
|
|
860
|
-
}
|
|
861
|
-
trackAndCompute() {
|
|
862
|
-
if (isSpyEnabled()) {
|
|
863
|
-
spyReport({
|
|
864
|
-
object: this.scope,
|
|
865
|
-
type: "compute",
|
|
866
|
-
name: this.name
|
|
867
|
-
});
|
|
868
|
-
}
|
|
869
|
-
const oldValue = this.value;
|
|
870
|
-
const wasSuspended =
|
|
871
|
-
/* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
|
|
872
|
-
const newValue = this.computeValue(true);
|
|
873
|
-
const changed = wasSuspended ||
|
|
874
|
-
isCaughtException(oldValue) ||
|
|
875
|
-
isCaughtException(newValue) ||
|
|
876
|
-
!this.equals(oldValue, newValue);
|
|
877
|
-
if (changed) {
|
|
878
|
-
this.value = newValue;
|
|
879
|
-
}
|
|
880
|
-
return changed;
|
|
881
|
-
}
|
|
882
|
-
computeValue(track) {
|
|
883
|
-
this.isComputing = true;
|
|
884
|
-
globalState.computationDepth++;
|
|
885
|
-
let res;
|
|
886
|
-
if (track) {
|
|
887
|
-
res = trackDerivedFunction(this, this.derivation, this.scope);
|
|
888
|
-
}
|
|
889
|
-
else {
|
|
890
|
-
if (globalState.disableErrorBoundaries === true) {
|
|
891
|
-
res = this.derivation.call(this.scope);
|
|
892
|
-
}
|
|
893
|
-
else {
|
|
894
|
-
try {
|
|
895
|
-
res = this.derivation.call(this.scope);
|
|
896
|
-
}
|
|
897
|
-
catch (e) {
|
|
898
|
-
res = new CaughtException(e);
|
|
899
|
-
}
|
|
900
|
-
}
|
|
901
|
-
}
|
|
902
|
-
globalState.computationDepth--;
|
|
903
|
-
this.isComputing = false;
|
|
904
|
-
return res;
|
|
905
|
-
}
|
|
906
|
-
suspend() {
|
|
907
|
-
if (!this.keepAlive) {
|
|
908
|
-
clearObserving(this);
|
|
909
|
-
this.value = undefined; // don't hold on to computed value!
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
observe(listener, fireImmediately) {
|
|
913
|
-
let firstTime = true;
|
|
914
|
-
let prevValue = undefined;
|
|
915
|
-
return autorun(() => {
|
|
916
|
-
let newValue = this.get();
|
|
917
|
-
if (!firstTime || fireImmediately) {
|
|
918
|
-
const prevU = untrackedStart();
|
|
919
|
-
listener({
|
|
920
|
-
type: "update",
|
|
921
|
-
object: this,
|
|
922
|
-
newValue,
|
|
923
|
-
oldValue: prevValue
|
|
924
|
-
});
|
|
925
|
-
untrackedEnd(prevU);
|
|
926
|
-
}
|
|
927
|
-
firstTime = false;
|
|
928
|
-
prevValue = newValue;
|
|
929
|
-
});
|
|
930
|
-
}
|
|
931
|
-
warnAboutUntrackedRead() {
|
|
932
|
-
if (process.env.NODE_ENV === "production")
|
|
933
|
-
return;
|
|
934
|
-
if (this.requiresReaction === true) {
|
|
935
|
-
fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
|
|
936
|
-
}
|
|
937
|
-
if (this.isTracing !== TraceMode.NONE) {
|
|
938
|
-
console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
|
|
939
|
-
}
|
|
940
|
-
if (globalState.computedRequiresReaction) {
|
|
941
|
-
console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
toJSON() {
|
|
945
|
-
return this.get();
|
|
946
|
-
}
|
|
947
|
-
toString() {
|
|
948
|
-
return `${this.name}[${this.derivation.toString()}]`;
|
|
949
|
-
}
|
|
950
|
-
valueOf() {
|
|
951
|
-
return toPrimitive(this.get());
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
ComputedValue.prototype[primitiveSymbol()] = ComputedValue.prototype.valueOf;
|
|
955
|
-
const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
|
|
956
|
-
|
|
957
|
-
var IDerivationState;
|
|
958
|
-
(function (IDerivationState) {
|
|
959
|
-
// before being run or (outside batch and not being observed)
|
|
960
|
-
// at this point derivation is not holding any data about dependency tree
|
|
961
|
-
IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
|
|
962
|
-
// no shallow dependency changed since last computation
|
|
963
|
-
// won't recalculate derivation
|
|
964
|
-
// this is what makes mobx fast
|
|
965
|
-
IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
|
|
966
|
-
// some deep dependency changed, but don't know if shallow dependency changed
|
|
967
|
-
// will require to check first if UP_TO_DATE or POSSIBLY_STALE
|
|
968
|
-
// currently only ComputedValue will propagate POSSIBLY_STALE
|
|
969
|
-
//
|
|
970
|
-
// having this state is second big optimization:
|
|
971
|
-
// don't have to recompute on every dependency change, but only when it's needed
|
|
972
|
-
IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
|
|
973
|
-
// A shallow dependency has changed since last computation and the derivation
|
|
974
|
-
// will need to recompute when it's needed next.
|
|
975
|
-
IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
|
|
976
|
-
})(IDerivationState || (IDerivationState = {}));
|
|
977
|
-
var TraceMode;
|
|
978
|
-
(function (TraceMode) {
|
|
979
|
-
TraceMode[TraceMode["NONE"] = 0] = "NONE";
|
|
980
|
-
TraceMode[TraceMode["LOG"] = 1] = "LOG";
|
|
981
|
-
TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
|
|
982
|
-
})(TraceMode || (TraceMode = {}));
|
|
983
|
-
class CaughtException {
|
|
984
|
-
constructor(cause) {
|
|
985
|
-
this.cause = cause;
|
|
986
|
-
// Empty
|
|
987
|
-
}
|
|
988
|
-
}
|
|
989
|
-
function isCaughtException(e) {
|
|
990
|
-
return e instanceof CaughtException;
|
|
991
|
-
}
|
|
992
|
-
/**
|
|
993
|
-
* Finds out whether any dependency of the derivation has actually changed.
|
|
994
|
-
* If dependenciesState is 1 then it will recalculate dependencies,
|
|
995
|
-
* if any dependency changed it will propagate it by changing dependenciesState to 2.
|
|
996
|
-
*
|
|
997
|
-
* By iterating over the dependencies in the same order that they were reported and
|
|
998
|
-
* stopping on the first change, all the recalculations are only called for ComputedValues
|
|
999
|
-
* that will be tracked by derivation. That is because we assume that if the first x
|
|
1000
|
-
* dependencies of the derivation doesn't change then the derivation should run the same way
|
|
1001
|
-
* up until accessing x-th dependency.
|
|
1002
|
-
*/
|
|
1003
|
-
function shouldCompute(derivation) {
|
|
1004
|
-
switch (derivation.dependenciesState) {
|
|
1005
|
-
case IDerivationState.UP_TO_DATE:
|
|
1006
|
-
return false;
|
|
1007
|
-
case IDerivationState.NOT_TRACKING:
|
|
1008
|
-
case IDerivationState.STALE:
|
|
1009
|
-
return true;
|
|
1010
|
-
case IDerivationState.POSSIBLY_STALE: {
|
|
1011
|
-
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
|
|
1012
|
-
const obs = derivation.observing, l = obs.length;
|
|
1013
|
-
for (let i = 0; i < l; i++) {
|
|
1014
|
-
const obj = obs[i];
|
|
1015
|
-
if (isComputedValue(obj)) {
|
|
1016
|
-
if (globalState.disableErrorBoundaries) {
|
|
1017
|
-
obj.get();
|
|
1018
|
-
}
|
|
1019
|
-
else {
|
|
1020
|
-
try {
|
|
1021
|
-
obj.get();
|
|
1022
|
-
}
|
|
1023
|
-
catch (e) {
|
|
1024
|
-
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
|
|
1025
|
-
untrackedEnd(prevUntracked);
|
|
1026
|
-
return true;
|
|
1027
|
-
}
|
|
1028
|
-
}
|
|
1029
|
-
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
|
|
1030
|
-
// and `derivation` is an observer of `obj`
|
|
1031
|
-
// invariantShouldCompute(derivation)
|
|
1032
|
-
if (derivation.dependenciesState === IDerivationState.STALE) {
|
|
1033
|
-
untrackedEnd(prevUntracked);
|
|
1034
|
-
return true;
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1145
|
+
try {
|
|
1146
|
+
this.setter.call(this.scope, value);
|
|
1147
|
+
}
|
|
1148
|
+
finally {
|
|
1149
|
+
this.isRunningSetter = false;
|
|
1037
1150
|
}
|
|
1038
|
-
changeDependenciesStateTo0(derivation);
|
|
1039
|
-
untrackedEnd(prevUntracked);
|
|
1040
|
-
return false;
|
|
1041
1151
|
}
|
|
1152
|
+
else
|
|
1153
|
+
invariant(false, process.env.NODE_ENV !== "production" &&
|
|
1154
|
+
`[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
|
|
1042
1155
|
}
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
// )
|
|
1051
|
-
// fail("Illegal dependency state")
|
|
1052
|
-
// }
|
|
1053
|
-
function isComputingDerivation() {
|
|
1054
|
-
return globalState.trackingDerivation !== null; // filter out actions inside computations
|
|
1055
|
-
}
|
|
1056
|
-
function checkIfStateModificationsAreAllowed(atom) {
|
|
1057
|
-
const hasObservers = atom.observers.length > 0;
|
|
1058
|
-
// Should never be possible to change an observed observable from inside computed, see #798
|
|
1059
|
-
if (globalState.computationDepth > 0 && hasObservers)
|
|
1060
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1061
|
-
`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
|
|
1062
|
-
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
|
|
1063
|
-
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
|
|
1064
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1065
|
-
(globalState.enforceActions
|
|
1066
|
-
? "Since strict-mode is enabled, changing observed observable values outside actions is not allowed. Please wrap the code in an `action` if this change is intended. Tried to modify: "
|
|
1067
|
-
: "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, the render function of a React component? Tried to modify: ") +
|
|
1068
|
-
atom.name);
|
|
1069
|
-
}
|
|
1070
|
-
/**
|
|
1071
|
-
* Executes the provided function `f` and tracks which observables are being accessed.
|
|
1072
|
-
* The tracking information is stored on the `derivation` object and the derivation is registered
|
|
1073
|
-
* as observer of any of the accessed observables.
|
|
1074
|
-
*/
|
|
1075
|
-
function trackDerivedFunction(derivation, f, context) {
|
|
1076
|
-
// pre allocate array allocation + room for variation in deps
|
|
1077
|
-
// array will be trimmed by bindDependencies
|
|
1078
|
-
changeDependenciesStateTo0(derivation);
|
|
1079
|
-
derivation.newObserving = new Array(derivation.observing.length + 100);
|
|
1080
|
-
derivation.unboundDepsCount = 0;
|
|
1081
|
-
derivation.runId = ++globalState.runId;
|
|
1082
|
-
const prevTracking = globalState.trackingDerivation;
|
|
1083
|
-
globalState.trackingDerivation = derivation;
|
|
1084
|
-
let result;
|
|
1085
|
-
if (globalState.disableErrorBoundaries === true) {
|
|
1086
|
-
result = f.call(context);
|
|
1087
|
-
}
|
|
1088
|
-
else {
|
|
1089
|
-
try {
|
|
1090
|
-
result = f.call(context);
|
|
1156
|
+
trackAndCompute() {
|
|
1157
|
+
if (isSpyEnabled()) {
|
|
1158
|
+
spyReport({
|
|
1159
|
+
object: this.scope,
|
|
1160
|
+
type: "compute",
|
|
1161
|
+
name: this.name
|
|
1162
|
+
});
|
|
1091
1163
|
}
|
|
1092
|
-
|
|
1093
|
-
|
|
1164
|
+
const oldValue = this.value;
|
|
1165
|
+
const wasSuspended =
|
|
1166
|
+
/* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
|
|
1167
|
+
const newValue = this.computeValue(true);
|
|
1168
|
+
const changed = wasSuspended ||
|
|
1169
|
+
isCaughtException(oldValue) ||
|
|
1170
|
+
isCaughtException(newValue) ||
|
|
1171
|
+
!this.equals(oldValue, newValue);
|
|
1172
|
+
if (changed) {
|
|
1173
|
+
this.value = newValue;
|
|
1094
1174
|
}
|
|
1175
|
+
return changed;
|
|
1095
1176
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
* update observing to be newObserving with unique observables
|
|
1103
|
-
* notify observers that become observed/unobserved
|
|
1104
|
-
*/
|
|
1105
|
-
function bindDependencies(derivation) {
|
|
1106
|
-
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
|
|
1107
|
-
const prevObserving = derivation.observing;
|
|
1108
|
-
const observing = (derivation.observing = derivation.newObserving);
|
|
1109
|
-
let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
|
|
1110
|
-
// Go through all new observables and check diffValue: (this list can contain duplicates):
|
|
1111
|
-
// 0: first occurrence, change to 1 and keep it
|
|
1112
|
-
// 1: extra occurrence, drop it
|
|
1113
|
-
let i0 = 0, l = derivation.unboundDepsCount;
|
|
1114
|
-
for (let i = 0; i < l; i++) {
|
|
1115
|
-
const dep = observing[i];
|
|
1116
|
-
if (dep.diffValue === 0) {
|
|
1117
|
-
dep.diffValue = 1;
|
|
1118
|
-
if (i0 !== i)
|
|
1119
|
-
observing[i0] = dep;
|
|
1120
|
-
i0++;
|
|
1177
|
+
computeValue(track) {
|
|
1178
|
+
this.isComputing = true;
|
|
1179
|
+
globalState.computationDepth++;
|
|
1180
|
+
let res;
|
|
1181
|
+
if (track) {
|
|
1182
|
+
res = trackDerivedFunction(this, this.derivation, this.scope);
|
|
1121
1183
|
}
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1184
|
+
else {
|
|
1185
|
+
if (globalState.disableErrorBoundaries === true) {
|
|
1186
|
+
res = this.derivation.call(this.scope);
|
|
1187
|
+
}
|
|
1188
|
+
else {
|
|
1189
|
+
try {
|
|
1190
|
+
res = this.derivation.call(this.scope);
|
|
1191
|
+
}
|
|
1192
|
+
catch (e) {
|
|
1193
|
+
res = new CaughtException(e);
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1126
1196
|
}
|
|
1197
|
+
globalState.computationDepth--;
|
|
1198
|
+
this.isComputing = false;
|
|
1199
|
+
return res;
|
|
1127
1200
|
}
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
// 1: it keeps being observed, don't want to notify it. change to 0
|
|
1133
|
-
l = prevObserving.length;
|
|
1134
|
-
while (l--) {
|
|
1135
|
-
const dep = prevObserving[l];
|
|
1136
|
-
if (dep.diffValue === 0) {
|
|
1137
|
-
removeObserver(dep, derivation);
|
|
1201
|
+
suspend() {
|
|
1202
|
+
if (!this.keepAlive) {
|
|
1203
|
+
clearObserving(this);
|
|
1204
|
+
this.value = undefined; // don't hold on to computed value!
|
|
1138
1205
|
}
|
|
1139
|
-
dep.diffValue = 0;
|
|
1140
1206
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1207
|
+
observe(listener, fireImmediately) {
|
|
1208
|
+
let firstTime = true;
|
|
1209
|
+
let prevValue = undefined;
|
|
1210
|
+
return autorun(() => {
|
|
1211
|
+
let newValue = this.get();
|
|
1212
|
+
if (!firstTime || fireImmediately) {
|
|
1213
|
+
const prevU = untrackedStart();
|
|
1214
|
+
listener({
|
|
1215
|
+
type: "update",
|
|
1216
|
+
object: this,
|
|
1217
|
+
newValue,
|
|
1218
|
+
oldValue: prevValue
|
|
1219
|
+
});
|
|
1220
|
+
untrackedEnd(prevU);
|
|
1221
|
+
}
|
|
1222
|
+
firstTime = false;
|
|
1223
|
+
prevValue = newValue;
|
|
1224
|
+
});
|
|
1225
|
+
}
|
|
1226
|
+
warnAboutUntrackedRead() {
|
|
1227
|
+
if (process.env.NODE_ENV === "production")
|
|
1228
|
+
return;
|
|
1229
|
+
if (this.requiresReaction === true) {
|
|
1230
|
+
fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
|
|
1231
|
+
}
|
|
1232
|
+
if (this.isTracing !== TraceMode.NONE) {
|
|
1233
|
+
console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
|
|
1234
|
+
}
|
|
1235
|
+
if (globalState.computedRequiresReaction) {
|
|
1236
|
+
console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
|
|
1149
1237
|
}
|
|
1150
1238
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
derivation.
|
|
1239
|
+
toJSON() {
|
|
1240
|
+
return this.get();
|
|
1241
|
+
}
|
|
1242
|
+
toString() {
|
|
1243
|
+
return `${this.name}[${this.derivation.toString()}]`;
|
|
1244
|
+
}
|
|
1245
|
+
valueOf() {
|
|
1246
|
+
return toPrimitive(this.get());
|
|
1156
1247
|
}
|
|
1157
1248
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
const obs = derivation.observing;
|
|
1161
|
-
derivation.observing = [];
|
|
1162
|
-
let i = obs.length;
|
|
1163
|
-
while (i--)
|
|
1164
|
-
removeObserver(obs[i], derivation);
|
|
1165
|
-
derivation.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
1166
|
-
}
|
|
1167
|
-
function untracked(action) {
|
|
1168
|
-
const prev = untrackedStart();
|
|
1169
|
-
const res = action();
|
|
1170
|
-
untrackedEnd(prev);
|
|
1171
|
-
return res;
|
|
1172
|
-
}
|
|
1173
|
-
function untrackedStart() {
|
|
1174
|
-
const prev = globalState.trackingDerivation;
|
|
1175
|
-
globalState.trackingDerivation = null;
|
|
1176
|
-
return prev;
|
|
1177
|
-
}
|
|
1178
|
-
function untrackedEnd(prev) {
|
|
1179
|
-
globalState.trackingDerivation = prev;
|
|
1180
|
-
}
|
|
1181
|
-
/**
|
|
1182
|
-
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
|
|
1183
|
-
*
|
|
1184
|
-
*/
|
|
1185
|
-
function changeDependenciesStateTo0(derivation) {
|
|
1186
|
-
if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
|
|
1187
|
-
return;
|
|
1188
|
-
derivation.dependenciesState = IDerivationState.UP_TO_DATE;
|
|
1189
|
-
const obs = derivation.observing;
|
|
1190
|
-
let i = obs.length;
|
|
1191
|
-
while (i--)
|
|
1192
|
-
obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
|
|
1193
|
-
}
|
|
1249
|
+
ComputedValue.prototype[primitiveSymbol()] = ComputedValue.prototype.valueOf;
|
|
1250
|
+
const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
|
|
1194
1251
|
|
|
1195
1252
|
/**
|
|
1196
1253
|
* These values will persist if global state is reset
|
|
@@ -1200,6 +1257,9 @@ const persistentKeys = [
|
|
|
1200
1257
|
"spyListeners",
|
|
1201
1258
|
"enforceActions",
|
|
1202
1259
|
"computedRequiresReaction",
|
|
1260
|
+
"reactionRequiresObservable",
|
|
1261
|
+
"observableRequiresReaction",
|
|
1262
|
+
"allowStateReads",
|
|
1203
1263
|
"disableErrorBoundaries",
|
|
1204
1264
|
"runId",
|
|
1205
1265
|
"UNCHANGED"
|
|
@@ -1260,6 +1320,11 @@ class MobXGlobals {
|
|
|
1260
1320
|
* To ensure that those functions stay pure.
|
|
1261
1321
|
*/
|
|
1262
1322
|
this.allowStateChanges = true;
|
|
1323
|
+
/**
|
|
1324
|
+
* Is it allowed to read observables at this point?
|
|
1325
|
+
* Used to hold the state needed for `observableRequiresReaction`
|
|
1326
|
+
*/
|
|
1327
|
+
this.allowStateReads = true;
|
|
1263
1328
|
/**
|
|
1264
1329
|
* If strict mode is enabled, state changes are by default not allowed
|
|
1265
1330
|
*/
|
|
@@ -1276,6 +1341,16 @@ class MobXGlobals {
|
|
|
1276
1341
|
* Warn if computed values are accessed outside a reactive context
|
|
1277
1342
|
*/
|
|
1278
1343
|
this.computedRequiresReaction = false;
|
|
1344
|
+
/**
|
|
1345
|
+
* (Experimental)
|
|
1346
|
+
* Warn if you try to create to derivation / reactive context without accessing any observable.
|
|
1347
|
+
*/
|
|
1348
|
+
this.reactionRequiresObservable = false;
|
|
1349
|
+
/**
|
|
1350
|
+
* (Experimental)
|
|
1351
|
+
* Warn if observables are accessed outside a reactive context
|
|
1352
|
+
*/
|
|
1353
|
+
this.observableRequiresReaction = false;
|
|
1279
1354
|
/**
|
|
1280
1355
|
* Allows overwriting of computed properties, useful in tests but not prod as it can cause
|
|
1281
1356
|
* memory leaks. See https://github.com/mobxjs/mobx/issues/1867
|
|
@@ -1456,6 +1531,7 @@ function endBatch() {
|
|
|
1456
1531
|
}
|
|
1457
1532
|
}
|
|
1458
1533
|
function reportObserved(observable) {
|
|
1534
|
+
checkIfStateReadsAreAllowed(observable);
|
|
1459
1535
|
const derivation = globalState.trackingDerivation;
|
|
1460
1536
|
if (derivation !== null) {
|
|
1461
1537
|
/**
|
|
@@ -1590,10 +1666,11 @@ function printDepTree(tree, lines, depth) {
|
|
|
1590
1666
|
}
|
|
1591
1667
|
|
|
1592
1668
|
class Reaction {
|
|
1593
|
-
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler) {
|
|
1669
|
+
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler, requiresObservable = false) {
|
|
1594
1670
|
this.name = name;
|
|
1595
1671
|
this.onInvalidate = onInvalidate;
|
|
1596
1672
|
this.errorHandler = errorHandler;
|
|
1673
|
+
this.requiresObservable = requiresObservable;
|
|
1597
1674
|
this.observing = []; // nodes we are looking at. Our value depends on these nodes
|
|
1598
1675
|
this.newObserving = [];
|
|
1599
1676
|
this.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
@@ -1935,7 +2012,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1935
2012
|
// normal autorun
|
|
1936
2013
|
reaction = new Reaction(name, function () {
|
|
1937
2014
|
this.track(reactionRunner);
|
|
1938
|
-
}, opts.onError);
|
|
2015
|
+
}, opts.onError, opts.requiresObservable);
|
|
1939
2016
|
}
|
|
1940
2017
|
else {
|
|
1941
2018
|
const scheduler = createSchedulerFromOptions(opts);
|
|
@@ -1950,7 +2027,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1950
2027
|
reaction.track(reactionRunner);
|
|
1951
2028
|
});
|
|
1952
2029
|
}
|
|
1953
|
-
}, opts.onError);
|
|
2030
|
+
}, opts.onError, opts.requiresObservable);
|
|
1954
2031
|
}
|
|
1955
2032
|
function reactionRunner() {
|
|
1956
2033
|
view(reaction);
|
|
@@ -1993,7 +2070,7 @@ function reaction(expression, effect, opts = EMPTY_OBJECT) {
|
|
|
1993
2070
|
isScheduled = true;
|
|
1994
2071
|
scheduler(reactionRunner);
|
|
1995
2072
|
}
|
|
1996
|
-
}, opts.onError);
|
|
2073
|
+
}, opts.onError, opts.requiresObservable);
|
|
1997
2074
|
function reactionRunner() {
|
|
1998
2075
|
isScheduled = false; // Q: move into reaction runner?
|
|
1999
2076
|
if (r.isDisposed)
|
|
@@ -2032,8 +2109,8 @@ function onBecomeUnobserved(thing, arg2, arg3) {
|
|
|
2032
2109
|
return interceptHook("onBecomeUnobserved", thing, arg2, arg3);
|
|
2033
2110
|
}
|
|
2034
2111
|
function interceptHook(hook, thing, arg2, arg3) {
|
|
2035
|
-
const atom = typeof
|
|
2036
|
-
const cb = typeof
|
|
2112
|
+
const atom = typeof arg3 === "function" ? getAtom(thing, arg2) : getAtom(thing);
|
|
2113
|
+
const cb = typeof arg3 === "function" ? arg3 : arg2;
|
|
2037
2114
|
const orig = atom[hook];
|
|
2038
2115
|
if (typeof orig !== "function")
|
|
2039
2116
|
return fail(process.env.NODE_ENV !== "production" && "Not an atom that can be (un)observed");
|
|
@@ -2047,7 +2124,7 @@ function interceptHook(hook, thing, arg2, arg3) {
|
|
|
2047
2124
|
}
|
|
2048
2125
|
|
|
2049
2126
|
function configure(options) {
|
|
2050
|
-
const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, arrayBuffer, reactionScheduler } = options;
|
|
2127
|
+
const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, arrayBuffer, reactionScheduler, reactionRequiresObservable, observableRequiresReaction } = options;
|
|
2051
2128
|
if (options.isolateGlobalState === true) {
|
|
2052
2129
|
isolateGlobalState();
|
|
2053
2130
|
}
|
|
@@ -2077,6 +2154,13 @@ function configure(options) {
|
|
|
2077
2154
|
if (computedRequiresReaction !== undefined) {
|
|
2078
2155
|
globalState.computedRequiresReaction = !!computedRequiresReaction;
|
|
2079
2156
|
}
|
|
2157
|
+
if (reactionRequiresObservable !== undefined) {
|
|
2158
|
+
globalState.reactionRequiresObservable = !!reactionRequiresObservable;
|
|
2159
|
+
}
|
|
2160
|
+
if (observableRequiresReaction !== undefined) {
|
|
2161
|
+
globalState.observableRequiresReaction = !!observableRequiresReaction;
|
|
2162
|
+
globalState.allowStateReads = !globalState.observableRequiresReaction;
|
|
2163
|
+
}
|
|
2080
2164
|
if (computedConfigurable !== undefined) {
|
|
2081
2165
|
globalState.computedConfigurable = !!computedConfigurable;
|
|
2082
2166
|
}
|
|
@@ -3297,9 +3381,16 @@ class ObservableMap {
|
|
|
3297
3381
|
return this._data.has(key);
|
|
3298
3382
|
}
|
|
3299
3383
|
has(key) {
|
|
3300
|
-
if (
|
|
3301
|
-
return this.
|
|
3302
|
-
|
|
3384
|
+
if (!globalState.trackingDerivation)
|
|
3385
|
+
return this._has(key);
|
|
3386
|
+
let entry = this._hasMap.get(key);
|
|
3387
|
+
if (!entry) {
|
|
3388
|
+
// todo: replace with atom (breaking change)
|
|
3389
|
+
const newEntry = (entry = new ObservableValue(this._has(key), referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false));
|
|
3390
|
+
this._hasMap.set(key, newEntry);
|
|
3391
|
+
onBecomeUnobserved(newEntry, () => this._hasMap.delete(key));
|
|
3392
|
+
}
|
|
3393
|
+
return entry.get();
|
|
3303
3394
|
}
|
|
3304
3395
|
set(key, value) {
|
|
3305
3396
|
const hasKey = this._has(key);
|
|
@@ -3361,16 +3452,10 @@ class ObservableMap {
|
|
|
3361
3452
|
return false;
|
|
3362
3453
|
}
|
|
3363
3454
|
_updateHasMapEntry(key, value) {
|
|
3364
|
-
// optimization; don't fill the hasMap if we are not observing, or remove entry if there are no observers anymore
|
|
3365
3455
|
let entry = this._hasMap.get(key);
|
|
3366
3456
|
if (entry) {
|
|
3367
3457
|
entry.setNewValue(value);
|
|
3368
3458
|
}
|
|
3369
|
-
else {
|
|
3370
|
-
entry = new ObservableValue(value, referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false);
|
|
3371
|
-
this._hasMap.set(key, entry);
|
|
3372
|
-
}
|
|
3373
|
-
return entry;
|
|
3374
3459
|
}
|
|
3375
3460
|
_updateValue(key, newValue) {
|
|
3376
3461
|
const observable = this._data.get(key);
|
|
@@ -3495,14 +3580,32 @@ class ObservableMap {
|
|
|
3495
3580
|
}
|
|
3496
3581
|
replace(values) {
|
|
3497
3582
|
transaction(() => {
|
|
3498
|
-
|
|
3499
|
-
// and delete them from the map, then merge the new map
|
|
3500
|
-
// this will cause reactions only on changed values
|
|
3501
|
-
const newKeys = getMapLikeKeys(values);
|
|
3583
|
+
const replacementMap = convertToMap(values);
|
|
3502
3584
|
const oldKeys = this._keys;
|
|
3503
|
-
const
|
|
3504
|
-
|
|
3505
|
-
|
|
3585
|
+
const newKeys = Array.from(replacementMap.keys());
|
|
3586
|
+
let keysChanged = false;
|
|
3587
|
+
for (let i = 0; i < oldKeys.length; i++) {
|
|
3588
|
+
const oldKey = oldKeys[i];
|
|
3589
|
+
// key order change
|
|
3590
|
+
if (oldKeys.length === newKeys.length && oldKey !== newKeys[i]) {
|
|
3591
|
+
keysChanged = true;
|
|
3592
|
+
}
|
|
3593
|
+
// deleted key
|
|
3594
|
+
if (!replacementMap.has(oldKey)) {
|
|
3595
|
+
keysChanged = true;
|
|
3596
|
+
this.delete(oldKey);
|
|
3597
|
+
}
|
|
3598
|
+
}
|
|
3599
|
+
replacementMap.forEach((value, key) => {
|
|
3600
|
+
// new key
|
|
3601
|
+
if (!this._data.has(key)) {
|
|
3602
|
+
keysChanged = true;
|
|
3603
|
+
}
|
|
3604
|
+
this.set(key, value);
|
|
3605
|
+
});
|
|
3606
|
+
if (keysChanged) {
|
|
3607
|
+
this._keys.replace(newKeys);
|
|
3608
|
+
}
|
|
3506
3609
|
});
|
|
3507
3610
|
return this;
|
|
3508
3611
|
}
|
|
@@ -4082,12 +4185,12 @@ function getDebugName(thing, property) {
|
|
|
4082
4185
|
}
|
|
4083
4186
|
|
|
4084
4187
|
const toString = Object.prototype.toString;
|
|
4085
|
-
function deepEqual(a, b) {
|
|
4086
|
-
return eq(a, b);
|
|
4188
|
+
function deepEqual(a, b, depth = -1) {
|
|
4189
|
+
return eq(a, b, depth);
|
|
4087
4190
|
}
|
|
4088
4191
|
// Copied from https://github.com/jashkenas/underscore/blob/5c237a7c682fb68fd5378203f0bf22dce1624854/underscore.js#L1186-L1289
|
|
4089
4192
|
// Internal recursive comparison function for `isEqual`.
|
|
4090
|
-
function eq(a, b, aStack, bStack) {
|
|
4193
|
+
function eq(a, b, depth, aStack, bStack) {
|
|
4091
4194
|
// Identical objects are equal. `0 === -0`, but they aren't identical.
|
|
4092
4195
|
// See the [Harmony `egal` proposal](http://wiki.ecmascript.org/doku.php?id=harmony:egal).
|
|
4093
4196
|
if (a === b)
|
|
@@ -4102,10 +4205,6 @@ function eq(a, b, aStack, bStack) {
|
|
|
4102
4205
|
const type = typeof a;
|
|
4103
4206
|
if (type !== "function" && type !== "object" && typeof b != "object")
|
|
4104
4207
|
return false;
|
|
4105
|
-
return deepEq(a, b, aStack, bStack);
|
|
4106
|
-
}
|
|
4107
|
-
// Internal recursive comparison function for `isEqual`.
|
|
4108
|
-
function deepEq(a, b, aStack, bStack) {
|
|
4109
4208
|
// Unwrap any wrapped objects.
|
|
4110
4209
|
a = unwrap(a);
|
|
4111
4210
|
b = unwrap(b);
|
|
@@ -4155,6 +4254,12 @@ function deepEq(a, b, aStack, bStack) {
|
|
|
4155
4254
|
return false;
|
|
4156
4255
|
}
|
|
4157
4256
|
}
|
|
4257
|
+
if (depth === 0) {
|
|
4258
|
+
return false;
|
|
4259
|
+
}
|
|
4260
|
+
else if (depth < 0) {
|
|
4261
|
+
depth = -1;
|
|
4262
|
+
}
|
|
4158
4263
|
// Assume equality for cyclic structures. The algorithm for detecting cyclic
|
|
4159
4264
|
// structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.
|
|
4160
4265
|
// Initializing stack of traversed objects.
|
|
@@ -4179,7 +4284,7 @@ function deepEq(a, b, aStack, bStack) {
|
|
|
4179
4284
|
return false;
|
|
4180
4285
|
// Deep compare the contents, ignoring non-numeric properties.
|
|
4181
4286
|
while (length--) {
|
|
4182
|
-
if (!eq(a[length], b[length], aStack, bStack))
|
|
4287
|
+
if (!eq(a[length], b[length], depth - 1, aStack, bStack))
|
|
4183
4288
|
return false;
|
|
4184
4289
|
}
|
|
4185
4290
|
}
|
|
@@ -4194,7 +4299,7 @@ function deepEq(a, b, aStack, bStack) {
|
|
|
4194
4299
|
while (length--) {
|
|
4195
4300
|
// Deep compare each member
|
|
4196
4301
|
key = keys[length];
|
|
4197
|
-
if (!(has$1(b, key) && eq(a[key], b[key], aStack, bStack)))
|
|
4302
|
+
if (!(has$1(b, key) && eq(a[key], b[key], depth - 1, aStack, bStack)))
|
|
4198
4303
|
return false;
|
|
4199
4304
|
}
|
|
4200
4305
|
}
|
|
@@ -4249,7 +4354,7 @@ try {
|
|
|
4249
4354
|
process.env.NODE_ENV;
|
|
4250
4355
|
}
|
|
4251
4356
|
catch (e) {
|
|
4252
|
-
const g =
|
|
4357
|
+
const g = getGlobal();
|
|
4253
4358
|
if (typeof process === "undefined")
|
|
4254
4359
|
g.process = {};
|
|
4255
4360
|
g.process.env = {};
|
|
@@ -4318,4 +4423,4 @@ if (process.env.NODE_ENV !== "production" &&
|
|
|
4318
4423
|
});
|
|
4319
4424
|
}
|
|
4320
4425
|
|
|
4321
|
-
export { $mobx, IDerivationState, ObservableMap, ObservableSet, Reaction, allowStateChanges as _allowStateChanges, allowStateChangesInsideComputed as _allowStateChangesInsideComputed, getAdministration as _getAdministration, getGlobalState as _getGlobalState, interceptReads as _interceptReads, isComputingDerivation as _isComputingDerivation, resetGlobalState as _resetGlobalState, action, autorun, comparer, computed, configure, createAtom, decorate, entries, extendObservable, extendShallowObservable, flow, get, getAtom, getDebugName, getDependencyTree, getObserverTree, has, intercept, isAction, isArrayLike, isObservableValue as isBoxedObservable, isComputed, isComputedProp, isObservable, isObservableArray, isObservableMap, isObservableObject, isObservableProp, isObservableSet, keys, observable, observe, onBecomeObserved, onBecomeUnobserved, onReactionError, reaction, remove, runInAction, set, spy, toJS, trace, transaction, untracked, values, when };
|
|
4426
|
+
export { $mobx, IDerivationState, ObservableMap, ObservableSet, Reaction, allowStateChanges as _allowStateChanges, allowStateChangesInsideComputed as _allowStateChangesInsideComputed, _endAction, getAdministration as _getAdministration, getGlobalState as _getGlobalState, interceptReads as _interceptReads, isComputingDerivation as _isComputingDerivation, resetGlobalState as _resetGlobalState, _startAction, action, autorun, comparer, computed, configure, createAtom, decorate, entries, extendObservable, extendShallowObservable, flow, get, getAtom, getDebugName, getDependencyTree, getObserverTree, has, intercept, isAction, isArrayLike, isObservableValue as isBoxedObservable, isComputed, isComputedProp, isObservable, isObservableArray, isObservableMap, isObservableObject, isObservableProp, isObservableSet, keys, observable, observe, onBecomeObserved, onBecomeUnobserved, onReactionError, reaction, remove, runInAction, set, spy, toJS, trace, transaction, untracked, values, when };
|