mobx 5.10.1 → 5.14.0
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/CHANGELOG.md +29 -1
- package/LICENSE +0 -0
- package/README.md +66 -52
- package/lib/api/action.d.ts +0 -0
- package/lib/api/actiondecorator.d.ts +0 -0
- package/lib/api/autorun.d.ts +5 -0
- package/lib/api/become-observed.d.ts +0 -0
- package/lib/api/computed.d.ts +0 -0
- package/lib/api/configure.d.ts +11 -0
- package/lib/api/decorate.d.ts +0 -0
- package/lib/api/extendobservable.d.ts +0 -0
- package/lib/api/extras.d.ts +0 -0
- package/lib/api/flow.d.ts +0 -0
- package/lib/api/intercept-read.d.ts +0 -0
- package/lib/api/intercept.d.ts +0 -0
- package/lib/api/iscomputed.d.ts +0 -0
- package/lib/api/isobservable.d.ts +0 -0
- package/lib/api/object-api.d.ts +2 -2
- package/lib/api/observable.d.ts +0 -0
- package/lib/api/observabledecorator.d.ts +0 -0
- package/lib/api/observe.d.ts +0 -0
- package/lib/api/tojs.d.ts +0 -0
- package/lib/api/trace.d.ts +0 -0
- package/lib/api/transaction.d.ts +0 -0
- package/lib/api/when.d.ts +5 -0
- package/lib/core/action.d.ts +13 -0
- package/lib/core/atom.d.ts +2 -2
- package/lib/core/computedvalue.d.ts +0 -0
- package/lib/core/derivation.d.ts +7 -0
- package/lib/core/globalstate.d.ts +22 -0
- package/lib/core/observable.d.ts +0 -0
- package/lib/core/reaction.d.ts +2 -1
- package/lib/core/spy.d.ts +0 -0
- package/lib/internal.d.ts +0 -0
- package/lib/mobx.d.ts +1 -1
- package/lib/mobx.es6.js +471 -382
- package/lib/mobx.js +485 -371
- package/lib/mobx.js.flow +20 -2
- package/lib/mobx.min.js +1 -1
- package/lib/mobx.module.js +485 -373
- package/lib/mobx.umd.js +485 -371
- package/lib/mobx.umd.min.js +1 -1
- package/lib/types/dynamicobject.d.ts +0 -0
- package/lib/types/intercept-utils.d.ts +0 -0
- package/lib/types/listen-utils.d.ts +0 -0
- package/lib/types/modifiers.d.ts +0 -0
- package/lib/types/observablearray.d.ts +0 -0
- package/lib/types/observablemap.d.ts +0 -0
- package/lib/types/observableobject.d.ts +0 -0
- package/lib/types/observableset.d.ts +0 -0
- package/lib/types/observablevalue.d.ts +0 -0
- package/lib/types/type-utils.d.ts +0 -0
- package/lib/utils/comparer.d.ts +0 -0
- package/lib/utils/decorators.d.ts +0 -0
- package/lib/utils/eq.d.ts +0 -0
- package/lib/utils/iterable.d.ts +0 -0
- package/lib/utils/utils.d.ts +0 -0
- package/package.json +7 -6
package/lib/mobx.es6.js
CHANGED
|
@@ -512,6 +512,274 @@ const computed = function computed(arg1, arg2, arg3) {
|
|
|
512
512
|
};
|
|
513
513
|
computed.struct = computedStructDecorator;
|
|
514
514
|
|
|
515
|
+
var IDerivationState;
|
|
516
|
+
(function (IDerivationState) {
|
|
517
|
+
// before being run or (outside batch and not being observed)
|
|
518
|
+
// at this point derivation is not holding any data about dependency tree
|
|
519
|
+
IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
|
|
520
|
+
// no shallow dependency changed since last computation
|
|
521
|
+
// won't recalculate derivation
|
|
522
|
+
// this is what makes mobx fast
|
|
523
|
+
IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
|
|
524
|
+
// some deep dependency changed, but don't know if shallow dependency changed
|
|
525
|
+
// will require to check first if UP_TO_DATE or POSSIBLY_STALE
|
|
526
|
+
// currently only ComputedValue will propagate POSSIBLY_STALE
|
|
527
|
+
//
|
|
528
|
+
// having this state is second big optimization:
|
|
529
|
+
// don't have to recompute on every dependency change, but only when it's needed
|
|
530
|
+
IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
|
|
531
|
+
// A shallow dependency has changed since last computation and the derivation
|
|
532
|
+
// will need to recompute when it's needed next.
|
|
533
|
+
IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
|
|
534
|
+
})(IDerivationState || (IDerivationState = {}));
|
|
535
|
+
var TraceMode;
|
|
536
|
+
(function (TraceMode) {
|
|
537
|
+
TraceMode[TraceMode["NONE"] = 0] = "NONE";
|
|
538
|
+
TraceMode[TraceMode["LOG"] = 1] = "LOG";
|
|
539
|
+
TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
|
|
540
|
+
})(TraceMode || (TraceMode = {}));
|
|
541
|
+
class CaughtException {
|
|
542
|
+
constructor(cause) {
|
|
543
|
+
this.cause = cause;
|
|
544
|
+
// Empty
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
function isCaughtException(e) {
|
|
548
|
+
return e instanceof CaughtException;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* Finds out whether any dependency of the derivation has actually changed.
|
|
552
|
+
* If dependenciesState is 1 then it will recalculate dependencies,
|
|
553
|
+
* if any dependency changed it will propagate it by changing dependenciesState to 2.
|
|
554
|
+
*
|
|
555
|
+
* By iterating over the dependencies in the same order that they were reported and
|
|
556
|
+
* stopping on the first change, all the recalculations are only called for ComputedValues
|
|
557
|
+
* that will be tracked by derivation. That is because we assume that if the first x
|
|
558
|
+
* dependencies of the derivation doesn't change then the derivation should run the same way
|
|
559
|
+
* up until accessing x-th dependency.
|
|
560
|
+
*/
|
|
561
|
+
function shouldCompute(derivation) {
|
|
562
|
+
switch (derivation.dependenciesState) {
|
|
563
|
+
case IDerivationState.UP_TO_DATE:
|
|
564
|
+
return false;
|
|
565
|
+
case IDerivationState.NOT_TRACKING:
|
|
566
|
+
case IDerivationState.STALE:
|
|
567
|
+
return true;
|
|
568
|
+
case IDerivationState.POSSIBLY_STALE: {
|
|
569
|
+
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
|
|
570
|
+
const obs = derivation.observing, l = obs.length;
|
|
571
|
+
for (let i = 0; i < l; i++) {
|
|
572
|
+
const obj = obs[i];
|
|
573
|
+
if (isComputedValue(obj)) {
|
|
574
|
+
if (globalState.disableErrorBoundaries) {
|
|
575
|
+
obj.get();
|
|
576
|
+
}
|
|
577
|
+
else {
|
|
578
|
+
try {
|
|
579
|
+
obj.get();
|
|
580
|
+
}
|
|
581
|
+
catch (e) {
|
|
582
|
+
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
|
|
583
|
+
untrackedEnd(prevUntracked);
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
|
|
588
|
+
// and `derivation` is an observer of `obj`
|
|
589
|
+
// invariantShouldCompute(derivation)
|
|
590
|
+
if (derivation.dependenciesState === IDerivationState.STALE) {
|
|
591
|
+
untrackedEnd(prevUntracked);
|
|
592
|
+
return true;
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
changeDependenciesStateTo0(derivation);
|
|
597
|
+
untrackedEnd(prevUntracked);
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
// function invariantShouldCompute(derivation: IDerivation) {
|
|
603
|
+
// const newDepState = (derivation as any).dependenciesState
|
|
604
|
+
// if (
|
|
605
|
+
// process.env.NODE_ENV === "production" &&
|
|
606
|
+
// (newDepState === IDerivationState.POSSIBLY_STALE ||
|
|
607
|
+
// newDepState === IDerivationState.NOT_TRACKING)
|
|
608
|
+
// )
|
|
609
|
+
// fail("Illegal dependency state")
|
|
610
|
+
// }
|
|
611
|
+
function isComputingDerivation() {
|
|
612
|
+
return globalState.trackingDerivation !== null; // filter out actions inside computations
|
|
613
|
+
}
|
|
614
|
+
function checkIfStateModificationsAreAllowed(atom) {
|
|
615
|
+
const hasObservers = atom.observers.size > 0;
|
|
616
|
+
// Should never be possible to change an observed observable from inside computed, see #798
|
|
617
|
+
if (globalState.computationDepth > 0 && hasObservers)
|
|
618
|
+
fail(process.env.NODE_ENV !== "production" &&
|
|
619
|
+
`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
|
|
620
|
+
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
|
|
621
|
+
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
|
|
622
|
+
fail(process.env.NODE_ENV !== "production" &&
|
|
623
|
+
(globalState.enforceActions
|
|
624
|
+
? "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: "
|
|
625
|
+
: "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: ") +
|
|
626
|
+
atom.name);
|
|
627
|
+
}
|
|
628
|
+
function checkIfStateReadsAreAllowed(observable) {
|
|
629
|
+
if (process.env.NODE_ENV !== "production" &&
|
|
630
|
+
!globalState.allowStateReads &&
|
|
631
|
+
globalState.observableRequiresReaction) {
|
|
632
|
+
console.warn(`[mobx] Observable ${observable.name} being read outside a reactive context`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
/**
|
|
636
|
+
* Executes the provided function `f` and tracks which observables are being accessed.
|
|
637
|
+
* The tracking information is stored on the `derivation` object and the derivation is registered
|
|
638
|
+
* as observer of any of the accessed observables.
|
|
639
|
+
*/
|
|
640
|
+
function trackDerivedFunction(derivation, f, context) {
|
|
641
|
+
const prevAllowStateReads = allowStateReadsStart(true);
|
|
642
|
+
// pre allocate array allocation + room for variation in deps
|
|
643
|
+
// array will be trimmed by bindDependencies
|
|
644
|
+
changeDependenciesStateTo0(derivation);
|
|
645
|
+
derivation.newObserving = new Array(derivation.observing.length + 100);
|
|
646
|
+
derivation.unboundDepsCount = 0;
|
|
647
|
+
derivation.runId = ++globalState.runId;
|
|
648
|
+
const prevTracking = globalState.trackingDerivation;
|
|
649
|
+
globalState.trackingDerivation = derivation;
|
|
650
|
+
let result;
|
|
651
|
+
if (globalState.disableErrorBoundaries === true) {
|
|
652
|
+
result = f.call(context);
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
try {
|
|
656
|
+
result = f.call(context);
|
|
657
|
+
}
|
|
658
|
+
catch (e) {
|
|
659
|
+
result = new CaughtException(e);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
globalState.trackingDerivation = prevTracking;
|
|
663
|
+
bindDependencies(derivation);
|
|
664
|
+
warnAboutDerivationWithoutDependencies(derivation);
|
|
665
|
+
allowStateReadsEnd(prevAllowStateReads);
|
|
666
|
+
return result;
|
|
667
|
+
}
|
|
668
|
+
function warnAboutDerivationWithoutDependencies(derivation) {
|
|
669
|
+
if (process.env.NODE_ENV === "production")
|
|
670
|
+
return;
|
|
671
|
+
if (derivation.observing.length !== 0)
|
|
672
|
+
return;
|
|
673
|
+
if (globalState.reactionRequiresObservable || derivation.requiresObservable) {
|
|
674
|
+
console.warn(`[mobx] Derivation ${derivation.name} is created/updated without reading any observable value`);
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
/**
|
|
678
|
+
* diffs newObserving with observing.
|
|
679
|
+
* update observing to be newObserving with unique observables
|
|
680
|
+
* notify observers that become observed/unobserved
|
|
681
|
+
*/
|
|
682
|
+
function bindDependencies(derivation) {
|
|
683
|
+
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
|
|
684
|
+
const prevObserving = derivation.observing;
|
|
685
|
+
const observing = (derivation.observing = derivation.newObserving);
|
|
686
|
+
let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
|
|
687
|
+
// Go through all new observables and check diffValue: (this list can contain duplicates):
|
|
688
|
+
// 0: first occurrence, change to 1 and keep it
|
|
689
|
+
// 1: extra occurrence, drop it
|
|
690
|
+
let i0 = 0, l = derivation.unboundDepsCount;
|
|
691
|
+
for (let i = 0; i < l; i++) {
|
|
692
|
+
const dep = observing[i];
|
|
693
|
+
if (dep.diffValue === 0) {
|
|
694
|
+
dep.diffValue = 1;
|
|
695
|
+
if (i0 !== i)
|
|
696
|
+
observing[i0] = dep;
|
|
697
|
+
i0++;
|
|
698
|
+
}
|
|
699
|
+
// Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
|
|
700
|
+
// not hitting the condition
|
|
701
|
+
if (dep.dependenciesState > lowestNewObservingDerivationState) {
|
|
702
|
+
lowestNewObservingDerivationState = dep.dependenciesState;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
observing.length = i0;
|
|
706
|
+
derivation.newObserving = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
|
|
707
|
+
// Go through all old observables and check diffValue: (it is unique after last bindDependencies)
|
|
708
|
+
// 0: it's not in new observables, unobserve it
|
|
709
|
+
// 1: it keeps being observed, don't want to notify it. change to 0
|
|
710
|
+
l = prevObserving.length;
|
|
711
|
+
while (l--) {
|
|
712
|
+
const dep = prevObserving[l];
|
|
713
|
+
if (dep.diffValue === 0) {
|
|
714
|
+
removeObserver(dep, derivation);
|
|
715
|
+
}
|
|
716
|
+
dep.diffValue = 0;
|
|
717
|
+
}
|
|
718
|
+
// Go through all new observables and check diffValue: (now it should be unique)
|
|
719
|
+
// 0: it was set to 0 in last loop. don't need to do anything.
|
|
720
|
+
// 1: it wasn't observed, let's observe it. set back to 0
|
|
721
|
+
while (i0--) {
|
|
722
|
+
const dep = observing[i0];
|
|
723
|
+
if (dep.diffValue === 1) {
|
|
724
|
+
dep.diffValue = 0;
|
|
725
|
+
addObserver(dep, derivation);
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
// Some new observed derivations may become stale during this derivation computation
|
|
729
|
+
// so they have had no chance to propagate staleness (#916)
|
|
730
|
+
if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
|
|
731
|
+
derivation.dependenciesState = lowestNewObservingDerivationState;
|
|
732
|
+
derivation.onBecomeStale();
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
function clearObserving(derivation) {
|
|
736
|
+
// invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
|
|
737
|
+
const obs = derivation.observing;
|
|
738
|
+
derivation.observing = [];
|
|
739
|
+
let i = obs.length;
|
|
740
|
+
while (i--)
|
|
741
|
+
removeObserver(obs[i], derivation);
|
|
742
|
+
derivation.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
743
|
+
}
|
|
744
|
+
function untracked(action) {
|
|
745
|
+
const prev = untrackedStart();
|
|
746
|
+
try {
|
|
747
|
+
return action();
|
|
748
|
+
}
|
|
749
|
+
finally {
|
|
750
|
+
untrackedEnd(prev);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
function untrackedStart() {
|
|
754
|
+
const prev = globalState.trackingDerivation;
|
|
755
|
+
globalState.trackingDerivation = null;
|
|
756
|
+
return prev;
|
|
757
|
+
}
|
|
758
|
+
function untrackedEnd(prev) {
|
|
759
|
+
globalState.trackingDerivation = prev;
|
|
760
|
+
}
|
|
761
|
+
function allowStateReadsStart(allowStateReads) {
|
|
762
|
+
const prev = globalState.allowStateReads;
|
|
763
|
+
globalState.allowStateReads = allowStateReads;
|
|
764
|
+
return prev;
|
|
765
|
+
}
|
|
766
|
+
function allowStateReadsEnd(prev) {
|
|
767
|
+
globalState.allowStateReads = prev;
|
|
768
|
+
}
|
|
769
|
+
/**
|
|
770
|
+
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
|
|
771
|
+
*
|
|
772
|
+
*/
|
|
773
|
+
function changeDependenciesStateTo0(derivation) {
|
|
774
|
+
if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
|
|
775
|
+
return;
|
|
776
|
+
derivation.dependenciesState = IDerivationState.UP_TO_DATE;
|
|
777
|
+
const obs = derivation.observing;
|
|
778
|
+
let i = obs.length;
|
|
779
|
+
while (i--)
|
|
780
|
+
obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
|
|
781
|
+
}
|
|
782
|
+
|
|
515
783
|
function createAction(actionName, fn, ref) {
|
|
516
784
|
if (process.env.NODE_ENV !== "production") {
|
|
517
785
|
invariant(typeof fn === "function", "`action` can only be invoked on functions");
|
|
@@ -525,25 +793,19 @@ function createAction(actionName, fn, ref) {
|
|
|
525
793
|
return res;
|
|
526
794
|
}
|
|
527
795
|
function executeAction(actionName, fn, scope, args) {
|
|
528
|
-
const runInfo =
|
|
529
|
-
let shouldSupressReactionError = true;
|
|
796
|
+
const runInfo = _startAction(actionName, scope, args);
|
|
530
797
|
try {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
798
|
+
return fn.apply(scope, args);
|
|
799
|
+
}
|
|
800
|
+
catch (err) {
|
|
801
|
+
runInfo.error = err;
|
|
802
|
+
throw err;
|
|
534
803
|
}
|
|
535
804
|
finally {
|
|
536
|
-
|
|
537
|
-
globalState.suppressReactionErrors = shouldSupressReactionError;
|
|
538
|
-
endAction(runInfo);
|
|
539
|
-
globalState.suppressReactionErrors = false;
|
|
540
|
-
}
|
|
541
|
-
else {
|
|
542
|
-
endAction(runInfo);
|
|
543
|
-
}
|
|
805
|
+
_endAction(runInfo);
|
|
544
806
|
}
|
|
545
807
|
}
|
|
546
|
-
function
|
|
808
|
+
function _startAction(actionName, scope, args) {
|
|
547
809
|
const notifySpy = isSpyEnabled() && !!actionName;
|
|
548
810
|
let startTime = 0;
|
|
549
811
|
if (notifySpy && process.env.NODE_ENV !== "production") {
|
|
@@ -563,19 +825,35 @@ function startAction(actionName, fn, scope, args) {
|
|
|
563
825
|
const prevDerivation = untrackedStart();
|
|
564
826
|
startBatch();
|
|
565
827
|
const prevAllowStateChanges = allowStateChangesStart(true);
|
|
566
|
-
|
|
828
|
+
const prevAllowStateReads = allowStateReadsStart(true);
|
|
829
|
+
const runInfo = {
|
|
567
830
|
prevDerivation,
|
|
568
831
|
prevAllowStateChanges,
|
|
832
|
+
prevAllowStateReads,
|
|
569
833
|
notifySpy,
|
|
570
|
-
startTime
|
|
834
|
+
startTime,
|
|
835
|
+
actionId: globalState.nextActionId++,
|
|
836
|
+
parentActionId: globalState.currentActionId
|
|
571
837
|
};
|
|
838
|
+
globalState.currentActionId = runInfo.actionId;
|
|
839
|
+
return runInfo;
|
|
572
840
|
}
|
|
573
|
-
function
|
|
841
|
+
function _endAction(runInfo) {
|
|
842
|
+
if (globalState.currentActionId !== runInfo.actionId) {
|
|
843
|
+
fail("invalid action stack. did you forget to finish an action?");
|
|
844
|
+
}
|
|
845
|
+
globalState.currentActionId = runInfo.parentActionId;
|
|
846
|
+
if (runInfo.error !== undefined) {
|
|
847
|
+
globalState.suppressReactionErrors = true;
|
|
848
|
+
}
|
|
574
849
|
allowStateChangesEnd(runInfo.prevAllowStateChanges);
|
|
850
|
+
allowStateReadsEnd(runInfo.prevAllowStateReads);
|
|
575
851
|
endBatch();
|
|
576
852
|
untrackedEnd(runInfo.prevDerivation);
|
|
577
|
-
if (runInfo.notifySpy && process.env.NODE_ENV !== "production")
|
|
853
|
+
if (runInfo.notifySpy && process.env.NODE_ENV !== "production") {
|
|
578
854
|
spyReportEnd({ time: Date.now() - runInfo.startTime });
|
|
855
|
+
}
|
|
856
|
+
globalState.suppressReactionErrors = false;
|
|
579
857
|
}
|
|
580
858
|
function allowStateChanges(allowStateChanges, func) {
|
|
581
859
|
const prev = allowStateChangesStart(allowStateChanges);
|
|
@@ -823,351 +1101,110 @@ class ComputedValue {
|
|
|
823
1101
|
this.setter.call(this.scope, value);
|
|
824
1102
|
}
|
|
825
1103
|
finally {
|
|
826
|
-
this.isRunningSetter = false;
|
|
827
|
-
}
|
|
828
|
-
}
|
|
829
|
-
else
|
|
830
|
-
invariant(false, process.env.NODE_ENV !== "production" &&
|
|
831
|
-
`[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
|
|
832
|
-
}
|
|
833
|
-
trackAndCompute() {
|
|
834
|
-
if (isSpyEnabled() && process.env.NODE_ENV !== "production") {
|
|
835
|
-
spyReport({
|
|
836
|
-
object: this.scope,
|
|
837
|
-
type: "compute",
|
|
838
|
-
name: this.name
|
|
839
|
-
});
|
|
840
|
-
}
|
|
841
|
-
const oldValue = this.value;
|
|
842
|
-
const wasSuspended =
|
|
843
|
-
/* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
|
|
844
|
-
const newValue = this.computeValue(true);
|
|
845
|
-
const changed = wasSuspended ||
|
|
846
|
-
isCaughtException(oldValue) ||
|
|
847
|
-
isCaughtException(newValue) ||
|
|
848
|
-
!this.equals(oldValue, newValue);
|
|
849
|
-
if (changed) {
|
|
850
|
-
this.value = newValue;
|
|
851
|
-
}
|
|
852
|
-
return changed;
|
|
853
|
-
}
|
|
854
|
-
computeValue(track) {
|
|
855
|
-
this.isComputing = true;
|
|
856
|
-
globalState.computationDepth++;
|
|
857
|
-
let res;
|
|
858
|
-
if (track) {
|
|
859
|
-
res = trackDerivedFunction(this, this.derivation, this.scope);
|
|
860
|
-
}
|
|
861
|
-
else {
|
|
862
|
-
if (globalState.disableErrorBoundaries === true) {
|
|
863
|
-
res = this.derivation.call(this.scope);
|
|
864
|
-
}
|
|
865
|
-
else {
|
|
866
|
-
try {
|
|
867
|
-
res = this.derivation.call(this.scope);
|
|
868
|
-
}
|
|
869
|
-
catch (e) {
|
|
870
|
-
res = new CaughtException(e);
|
|
871
|
-
}
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
globalState.computationDepth--;
|
|
875
|
-
this.isComputing = false;
|
|
876
|
-
return res;
|
|
877
|
-
}
|
|
878
|
-
suspend() {
|
|
879
|
-
if (!this.keepAlive) {
|
|
880
|
-
clearObserving(this);
|
|
881
|
-
this.value = undefined; // don't hold on to computed value!
|
|
882
|
-
}
|
|
883
|
-
}
|
|
884
|
-
observe(listener, fireImmediately) {
|
|
885
|
-
let firstTime = true;
|
|
886
|
-
let prevValue = undefined;
|
|
887
|
-
return autorun(() => {
|
|
888
|
-
let newValue = this.get();
|
|
889
|
-
if (!firstTime || fireImmediately) {
|
|
890
|
-
const prevU = untrackedStart();
|
|
891
|
-
listener({
|
|
892
|
-
type: "update",
|
|
893
|
-
object: this,
|
|
894
|
-
newValue,
|
|
895
|
-
oldValue: prevValue
|
|
896
|
-
});
|
|
897
|
-
untrackedEnd(prevU);
|
|
898
|
-
}
|
|
899
|
-
firstTime = false;
|
|
900
|
-
prevValue = newValue;
|
|
901
|
-
});
|
|
902
|
-
}
|
|
903
|
-
warnAboutUntrackedRead() {
|
|
904
|
-
if (process.env.NODE_ENV === "production")
|
|
905
|
-
return;
|
|
906
|
-
if (this.requiresReaction === true) {
|
|
907
|
-
fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
|
|
908
|
-
}
|
|
909
|
-
if (this.isTracing !== TraceMode.NONE) {
|
|
910
|
-
console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
|
|
911
|
-
}
|
|
912
|
-
if (globalState.computedRequiresReaction) {
|
|
913
|
-
console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
|
|
914
|
-
}
|
|
915
|
-
}
|
|
916
|
-
toJSON() {
|
|
917
|
-
return this.get();
|
|
918
|
-
}
|
|
919
|
-
toString() {
|
|
920
|
-
return `${this.name}[${this.derivation.toString()}]`;
|
|
921
|
-
}
|
|
922
|
-
valueOf() {
|
|
923
|
-
return toPrimitive(this.get());
|
|
924
|
-
}
|
|
925
|
-
[Symbol.toPrimitive]() {
|
|
926
|
-
return this.valueOf();
|
|
927
|
-
}
|
|
928
|
-
}
|
|
929
|
-
const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
|
|
930
|
-
|
|
931
|
-
var IDerivationState;
|
|
932
|
-
(function (IDerivationState) {
|
|
933
|
-
// before being run or (outside batch and not being observed)
|
|
934
|
-
// at this point derivation is not holding any data about dependency tree
|
|
935
|
-
IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
|
|
936
|
-
// no shallow dependency changed since last computation
|
|
937
|
-
// won't recalculate derivation
|
|
938
|
-
// this is what makes mobx fast
|
|
939
|
-
IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
|
|
940
|
-
// some deep dependency changed, but don't know if shallow dependency changed
|
|
941
|
-
// will require to check first if UP_TO_DATE or POSSIBLY_STALE
|
|
942
|
-
// currently only ComputedValue will propagate POSSIBLY_STALE
|
|
943
|
-
//
|
|
944
|
-
// having this state is second big optimization:
|
|
945
|
-
// don't have to recompute on every dependency change, but only when it's needed
|
|
946
|
-
IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
|
|
947
|
-
// A shallow dependency has changed since last computation and the derivation
|
|
948
|
-
// will need to recompute when it's needed next.
|
|
949
|
-
IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
|
|
950
|
-
})(IDerivationState || (IDerivationState = {}));
|
|
951
|
-
var TraceMode;
|
|
952
|
-
(function (TraceMode) {
|
|
953
|
-
TraceMode[TraceMode["NONE"] = 0] = "NONE";
|
|
954
|
-
TraceMode[TraceMode["LOG"] = 1] = "LOG";
|
|
955
|
-
TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
|
|
956
|
-
})(TraceMode || (TraceMode = {}));
|
|
957
|
-
class CaughtException {
|
|
958
|
-
constructor(cause) {
|
|
959
|
-
this.cause = cause;
|
|
960
|
-
// Empty
|
|
961
|
-
}
|
|
962
|
-
}
|
|
963
|
-
function isCaughtException(e) {
|
|
964
|
-
return e instanceof CaughtException;
|
|
965
|
-
}
|
|
966
|
-
/**
|
|
967
|
-
* Finds out whether any dependency of the derivation has actually changed.
|
|
968
|
-
* If dependenciesState is 1 then it will recalculate dependencies,
|
|
969
|
-
* if any dependency changed it will propagate it by changing dependenciesState to 2.
|
|
970
|
-
*
|
|
971
|
-
* By iterating over the dependencies in the same order that they were reported and
|
|
972
|
-
* stopping on the first change, all the recalculations are only called for ComputedValues
|
|
973
|
-
* that will be tracked by derivation. That is because we assume that if the first x
|
|
974
|
-
* dependencies of the derivation doesn't change then the derivation should run the same way
|
|
975
|
-
* up until accessing x-th dependency.
|
|
976
|
-
*/
|
|
977
|
-
function shouldCompute(derivation) {
|
|
978
|
-
switch (derivation.dependenciesState) {
|
|
979
|
-
case IDerivationState.UP_TO_DATE:
|
|
980
|
-
return false;
|
|
981
|
-
case IDerivationState.NOT_TRACKING:
|
|
982
|
-
case IDerivationState.STALE:
|
|
983
|
-
return true;
|
|
984
|
-
case IDerivationState.POSSIBLY_STALE: {
|
|
985
|
-
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
|
|
986
|
-
const obs = derivation.observing, l = obs.length;
|
|
987
|
-
for (let i = 0; i < l; i++) {
|
|
988
|
-
const obj = obs[i];
|
|
989
|
-
if (isComputedValue(obj)) {
|
|
990
|
-
if (globalState.disableErrorBoundaries) {
|
|
991
|
-
obj.get();
|
|
992
|
-
}
|
|
993
|
-
else {
|
|
994
|
-
try {
|
|
995
|
-
obj.get();
|
|
996
|
-
}
|
|
997
|
-
catch (e) {
|
|
998
|
-
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
|
|
999
|
-
untrackedEnd(prevUntracked);
|
|
1000
|
-
return true;
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
|
|
1004
|
-
// and `derivation` is an observer of `obj`
|
|
1005
|
-
// invariantShouldCompute(derivation)
|
|
1006
|
-
if (derivation.dependenciesState === IDerivationState.STALE) {
|
|
1007
|
-
untrackedEnd(prevUntracked);
|
|
1008
|
-
return true;
|
|
1009
|
-
}
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
changeDependenciesStateTo0(derivation);
|
|
1013
|
-
untrackedEnd(prevUntracked);
|
|
1014
|
-
return false;
|
|
1104
|
+
this.isRunningSetter = false;
|
|
1105
|
+
}
|
|
1015
1106
|
}
|
|
1107
|
+
else
|
|
1108
|
+
invariant(false, process.env.NODE_ENV !== "production" &&
|
|
1109
|
+
`[ComputedValue '${this.name}'] It is not possible to assign a new value to a computed value.`);
|
|
1016
1110
|
}
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
// )
|
|
1025
|
-
// fail("Illegal dependency state")
|
|
1026
|
-
// }
|
|
1027
|
-
function isComputingDerivation() {
|
|
1028
|
-
return globalState.trackingDerivation !== null; // filter out actions inside computations
|
|
1029
|
-
}
|
|
1030
|
-
function checkIfStateModificationsAreAllowed(atom) {
|
|
1031
|
-
const hasObservers = atom.observers.size > 0;
|
|
1032
|
-
// Should never be possible to change an observed observable from inside computed, see #798
|
|
1033
|
-
if (globalState.computationDepth > 0 && hasObservers)
|
|
1034
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1035
|
-
`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
|
|
1036
|
-
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
|
|
1037
|
-
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
|
|
1038
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1039
|
-
(globalState.enforceActions
|
|
1040
|
-
? "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: "
|
|
1041
|
-
: "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: ") +
|
|
1042
|
-
atom.name);
|
|
1043
|
-
}
|
|
1044
|
-
/**
|
|
1045
|
-
* Executes the provided function `f` and tracks which observables are being accessed.
|
|
1046
|
-
* The tracking information is stored on the `derivation` object and the derivation is registered
|
|
1047
|
-
* as observer of any of the accessed observables.
|
|
1048
|
-
*/
|
|
1049
|
-
function trackDerivedFunction(derivation, f, context) {
|
|
1050
|
-
// pre allocate array allocation + room for variation in deps
|
|
1051
|
-
// array will be trimmed by bindDependencies
|
|
1052
|
-
changeDependenciesStateTo0(derivation);
|
|
1053
|
-
derivation.newObserving = new Array(derivation.observing.length + 100);
|
|
1054
|
-
derivation.unboundDepsCount = 0;
|
|
1055
|
-
derivation.runId = ++globalState.runId;
|
|
1056
|
-
const prevTracking = globalState.trackingDerivation;
|
|
1057
|
-
globalState.trackingDerivation = derivation;
|
|
1058
|
-
let result;
|
|
1059
|
-
if (globalState.disableErrorBoundaries === true) {
|
|
1060
|
-
result = f.call(context);
|
|
1061
|
-
}
|
|
1062
|
-
else {
|
|
1063
|
-
try {
|
|
1064
|
-
result = f.call(context);
|
|
1111
|
+
trackAndCompute() {
|
|
1112
|
+
if (isSpyEnabled() && process.env.NODE_ENV !== "production") {
|
|
1113
|
+
spyReport({
|
|
1114
|
+
object: this.scope,
|
|
1115
|
+
type: "compute",
|
|
1116
|
+
name: this.name
|
|
1117
|
+
});
|
|
1065
1118
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1119
|
+
const oldValue = this.value;
|
|
1120
|
+
const wasSuspended =
|
|
1121
|
+
/* see #1208 */ this.dependenciesState === IDerivationState.NOT_TRACKING;
|
|
1122
|
+
const newValue = this.computeValue(true);
|
|
1123
|
+
const changed = wasSuspended ||
|
|
1124
|
+
isCaughtException(oldValue) ||
|
|
1125
|
+
isCaughtException(newValue) ||
|
|
1126
|
+
!this.equals(oldValue, newValue);
|
|
1127
|
+
if (changed) {
|
|
1128
|
+
this.value = newValue;
|
|
1068
1129
|
}
|
|
1130
|
+
return changed;
|
|
1069
1131
|
}
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
* update observing to be newObserving with unique observables
|
|
1077
|
-
* notify observers that become observed/unobserved
|
|
1078
|
-
*/
|
|
1079
|
-
function bindDependencies(derivation) {
|
|
1080
|
-
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
|
|
1081
|
-
const prevObserving = derivation.observing;
|
|
1082
|
-
const observing = (derivation.observing = derivation.newObserving);
|
|
1083
|
-
let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
|
|
1084
|
-
// Go through all new observables and check diffValue: (this list can contain duplicates):
|
|
1085
|
-
// 0: first occurrence, change to 1 and keep it
|
|
1086
|
-
// 1: extra occurrence, drop it
|
|
1087
|
-
let i0 = 0, l = derivation.unboundDepsCount;
|
|
1088
|
-
for (let i = 0; i < l; i++) {
|
|
1089
|
-
const dep = observing[i];
|
|
1090
|
-
if (dep.diffValue === 0) {
|
|
1091
|
-
dep.diffValue = 1;
|
|
1092
|
-
if (i0 !== i)
|
|
1093
|
-
observing[i0] = dep;
|
|
1094
|
-
i0++;
|
|
1132
|
+
computeValue(track) {
|
|
1133
|
+
this.isComputing = true;
|
|
1134
|
+
globalState.computationDepth++;
|
|
1135
|
+
let res;
|
|
1136
|
+
if (track) {
|
|
1137
|
+
res = trackDerivedFunction(this, this.derivation, this.scope);
|
|
1095
1138
|
}
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1139
|
+
else {
|
|
1140
|
+
if (globalState.disableErrorBoundaries === true) {
|
|
1141
|
+
res = this.derivation.call(this.scope);
|
|
1142
|
+
}
|
|
1143
|
+
else {
|
|
1144
|
+
try {
|
|
1145
|
+
res = this.derivation.call(this.scope);
|
|
1146
|
+
}
|
|
1147
|
+
catch (e) {
|
|
1148
|
+
res = new CaughtException(e);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1100
1151
|
}
|
|
1152
|
+
globalState.computationDepth--;
|
|
1153
|
+
this.isComputing = false;
|
|
1154
|
+
return res;
|
|
1101
1155
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
// 1: it keeps being observed, don't want to notify it. change to 0
|
|
1107
|
-
l = prevObserving.length;
|
|
1108
|
-
while (l--) {
|
|
1109
|
-
const dep = prevObserving[l];
|
|
1110
|
-
if (dep.diffValue === 0) {
|
|
1111
|
-
removeObserver(dep, derivation);
|
|
1156
|
+
suspend() {
|
|
1157
|
+
if (!this.keepAlive) {
|
|
1158
|
+
clearObserving(this);
|
|
1159
|
+
this.value = undefined; // don't hold on to computed value!
|
|
1112
1160
|
}
|
|
1113
|
-
dep.diffValue = 0;
|
|
1114
1161
|
}
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1162
|
+
observe(listener, fireImmediately) {
|
|
1163
|
+
let firstTime = true;
|
|
1164
|
+
let prevValue = undefined;
|
|
1165
|
+
return autorun(() => {
|
|
1166
|
+
let newValue = this.get();
|
|
1167
|
+
if (!firstTime || fireImmediately) {
|
|
1168
|
+
const prevU = untrackedStart();
|
|
1169
|
+
listener({
|
|
1170
|
+
type: "update",
|
|
1171
|
+
object: this,
|
|
1172
|
+
newValue,
|
|
1173
|
+
oldValue: prevValue
|
|
1174
|
+
});
|
|
1175
|
+
untrackedEnd(prevU);
|
|
1176
|
+
}
|
|
1177
|
+
firstTime = false;
|
|
1178
|
+
prevValue = newValue;
|
|
1179
|
+
});
|
|
1180
|
+
}
|
|
1181
|
+
warnAboutUntrackedRead() {
|
|
1182
|
+
if (process.env.NODE_ENV === "production")
|
|
1183
|
+
return;
|
|
1184
|
+
if (this.requiresReaction === true) {
|
|
1185
|
+
fail(`[mobx] Computed value ${this.name} is read outside a reactive context`);
|
|
1186
|
+
}
|
|
1187
|
+
if (this.isTracing !== TraceMode.NONE) {
|
|
1188
|
+
console.log(`[mobx.trace] '${this.name}' is being read outside a reactive context. Doing a full recompute`);
|
|
1189
|
+
}
|
|
1190
|
+
if (globalState.computedRequiresReaction) {
|
|
1191
|
+
console.warn(`[mobx] Computed value ${this.name} is being read outside a reactive context. Doing a full recompute`);
|
|
1123
1192
|
}
|
|
1124
1193
|
}
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
|
|
1128
|
-
derivation.dependenciesState = lowestNewObservingDerivationState;
|
|
1129
|
-
derivation.onBecomeStale();
|
|
1194
|
+
toJSON() {
|
|
1195
|
+
return this.get();
|
|
1130
1196
|
}
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
// invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
|
|
1134
|
-
const obs = derivation.observing;
|
|
1135
|
-
derivation.observing = [];
|
|
1136
|
-
let i = obs.length;
|
|
1137
|
-
while (i--)
|
|
1138
|
-
removeObserver(obs[i], derivation);
|
|
1139
|
-
derivation.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
1140
|
-
}
|
|
1141
|
-
function untracked(action) {
|
|
1142
|
-
const prev = untrackedStart();
|
|
1143
|
-
try {
|
|
1144
|
-
return action();
|
|
1197
|
+
toString() {
|
|
1198
|
+
return `${this.name}[${this.derivation.toString()}]`;
|
|
1145
1199
|
}
|
|
1146
|
-
|
|
1147
|
-
|
|
1200
|
+
valueOf() {
|
|
1201
|
+
return toPrimitive(this.get());
|
|
1202
|
+
}
|
|
1203
|
+
[Symbol.toPrimitive]() {
|
|
1204
|
+
return this.valueOf();
|
|
1148
1205
|
}
|
|
1149
1206
|
}
|
|
1150
|
-
|
|
1151
|
-
const prev = globalState.trackingDerivation;
|
|
1152
|
-
globalState.trackingDerivation = null;
|
|
1153
|
-
return prev;
|
|
1154
|
-
}
|
|
1155
|
-
function untrackedEnd(prev) {
|
|
1156
|
-
globalState.trackingDerivation = prev;
|
|
1157
|
-
}
|
|
1158
|
-
/**
|
|
1159
|
-
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
|
|
1160
|
-
*
|
|
1161
|
-
*/
|
|
1162
|
-
function changeDependenciesStateTo0(derivation) {
|
|
1163
|
-
if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
|
|
1164
|
-
return;
|
|
1165
|
-
derivation.dependenciesState = IDerivationState.UP_TO_DATE;
|
|
1166
|
-
const obs = derivation.observing;
|
|
1167
|
-
let i = obs.length;
|
|
1168
|
-
while (i--)
|
|
1169
|
-
obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
|
|
1170
|
-
}
|
|
1207
|
+
const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
|
|
1171
1208
|
|
|
1172
1209
|
/**
|
|
1173
1210
|
* These values will persist if global state is reset
|
|
@@ -1177,6 +1214,9 @@ const persistentKeys = [
|
|
|
1177
1214
|
"spyListeners",
|
|
1178
1215
|
"enforceActions",
|
|
1179
1216
|
"computedRequiresReaction",
|
|
1217
|
+
"reactionRequiresObservable",
|
|
1218
|
+
"observableRequiresReaction",
|
|
1219
|
+
"allowStateReads",
|
|
1180
1220
|
"disableErrorBoundaries",
|
|
1181
1221
|
"runId",
|
|
1182
1222
|
"UNCHANGED"
|
|
@@ -1237,6 +1277,11 @@ class MobXGlobals {
|
|
|
1237
1277
|
* To ensure that those functions stay pure.
|
|
1238
1278
|
*/
|
|
1239
1279
|
this.allowStateChanges = true;
|
|
1280
|
+
/**
|
|
1281
|
+
* Is it allowed to read observables at this point?
|
|
1282
|
+
* Used to hold the state needed for `observableRequiresReaction`
|
|
1283
|
+
*/
|
|
1284
|
+
this.allowStateReads = true;
|
|
1240
1285
|
/**
|
|
1241
1286
|
* If strict mode is enabled, state changes are by default not allowed
|
|
1242
1287
|
*/
|
|
@@ -1253,16 +1298,39 @@ class MobXGlobals {
|
|
|
1253
1298
|
* Warn if computed values are accessed outside a reactive context
|
|
1254
1299
|
*/
|
|
1255
1300
|
this.computedRequiresReaction = false;
|
|
1301
|
+
/**
|
|
1302
|
+
* (Experimental)
|
|
1303
|
+
* Warn if you try to create to derivation / reactive context without accessing any observable.
|
|
1304
|
+
*/
|
|
1305
|
+
this.reactionRequiresObservable = false;
|
|
1306
|
+
/**
|
|
1307
|
+
* (Experimental)
|
|
1308
|
+
* Warn if observables are accessed outside a reactive context
|
|
1309
|
+
*/
|
|
1310
|
+
this.observableRequiresReaction = false;
|
|
1311
|
+
/**
|
|
1312
|
+
* Allows overwriting of computed properties, useful in tests but not prod as it can cause
|
|
1313
|
+
* memory leaks. See https://github.com/mobxjs/mobx/issues/1867
|
|
1314
|
+
*/
|
|
1315
|
+
this.computedConfigurable = false;
|
|
1256
1316
|
/*
|
|
1257
1317
|
* Don't catch and rethrow exceptions. This is useful for inspecting the state of
|
|
1258
1318
|
* the stack when an exception occurs while debugging.
|
|
1259
1319
|
*/
|
|
1260
1320
|
this.disableErrorBoundaries = false;
|
|
1261
1321
|
/*
|
|
1262
|
-
* If true, we are already handling an exception in an action. Any errors in reactions should be
|
|
1322
|
+
* If true, we are already handling an exception in an action. Any errors in reactions should be suppressed, as
|
|
1263
1323
|
* they are not the cause, see: https://github.com/mobxjs/mobx/issues/1836
|
|
1264
1324
|
*/
|
|
1265
1325
|
this.suppressReactionErrors = false;
|
|
1326
|
+
/*
|
|
1327
|
+
* Current action id.
|
|
1328
|
+
*/
|
|
1329
|
+
this.currentActionId = 0;
|
|
1330
|
+
/*
|
|
1331
|
+
* Next action id.
|
|
1332
|
+
*/
|
|
1333
|
+
this.nextActionId = 1;
|
|
1266
1334
|
}
|
|
1267
1335
|
}
|
|
1268
1336
|
let canMergeGlobalState = true;
|
|
@@ -1318,8 +1386,15 @@ function resetGlobalState() {
|
|
|
1318
1386
|
globalState[key] = defaultGlobals[key];
|
|
1319
1387
|
globalState.allowStateChanges = !globalState.enforceActions;
|
|
1320
1388
|
}
|
|
1389
|
+
const mockGlobal = {};
|
|
1321
1390
|
function getGlobal() {
|
|
1322
|
-
|
|
1391
|
+
if (typeof window !== "undefined") {
|
|
1392
|
+
return window;
|
|
1393
|
+
}
|
|
1394
|
+
if (typeof global !== "undefined") {
|
|
1395
|
+
return global;
|
|
1396
|
+
}
|
|
1397
|
+
return mockGlobal;
|
|
1323
1398
|
}
|
|
1324
1399
|
|
|
1325
1400
|
function hasObservers(observable) {
|
|
@@ -1407,6 +1482,7 @@ function endBatch() {
|
|
|
1407
1482
|
}
|
|
1408
1483
|
}
|
|
1409
1484
|
function reportObserved(observable) {
|
|
1485
|
+
checkIfStateReadsAreAllowed(observable);
|
|
1410
1486
|
const derivation = globalState.trackingDerivation;
|
|
1411
1487
|
if (derivation !== null) {
|
|
1412
1488
|
/**
|
|
@@ -1534,10 +1610,11 @@ function printDepTree(tree, lines, depth) {
|
|
|
1534
1610
|
}
|
|
1535
1611
|
|
|
1536
1612
|
class Reaction {
|
|
1537
|
-
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler) {
|
|
1613
|
+
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler, requiresObservable = false) {
|
|
1538
1614
|
this.name = name;
|
|
1539
1615
|
this.onInvalidate = onInvalidate;
|
|
1540
1616
|
this.errorHandler = errorHandler;
|
|
1617
|
+
this.requiresObservable = requiresObservable;
|
|
1541
1618
|
this.observing = []; // nodes we are looking at. Our value depends on these nodes
|
|
1542
1619
|
this.newObserving = [];
|
|
1543
1620
|
this.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
@@ -1896,7 +1973,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1896
1973
|
// normal autorun
|
|
1897
1974
|
reaction = new Reaction(name, function () {
|
|
1898
1975
|
this.track(reactionRunner);
|
|
1899
|
-
}, opts.onError);
|
|
1976
|
+
}, opts.onError, opts.requiresObservable);
|
|
1900
1977
|
}
|
|
1901
1978
|
else {
|
|
1902
1979
|
const scheduler = createSchedulerFromOptions(opts);
|
|
@@ -1911,7 +1988,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1911
1988
|
reaction.track(reactionRunner);
|
|
1912
1989
|
});
|
|
1913
1990
|
}
|
|
1914
|
-
}, opts.onError);
|
|
1991
|
+
}, opts.onError, opts.requiresObservable);
|
|
1915
1992
|
}
|
|
1916
1993
|
function reactionRunner() {
|
|
1917
1994
|
view(reaction);
|
|
@@ -1950,7 +2027,7 @@ function reaction(expression, effect, opts = EMPTY_OBJECT) {
|
|
|
1950
2027
|
isScheduled = true;
|
|
1951
2028
|
scheduler(reactionRunner);
|
|
1952
2029
|
}
|
|
1953
|
-
}, opts.onError);
|
|
2030
|
+
}, opts.onError, opts.requiresObservable);
|
|
1954
2031
|
function reactionRunner() {
|
|
1955
2032
|
isScheduled = false; // Q: move into reaction runner?
|
|
1956
2033
|
if (r.isDisposed)
|
|
@@ -1989,8 +2066,8 @@ function onBecomeUnobserved(thing, arg2, arg3) {
|
|
|
1989
2066
|
return interceptHook("onBecomeUnobserved", thing, arg2, arg3);
|
|
1990
2067
|
}
|
|
1991
2068
|
function interceptHook(hook, thing, arg2, arg3) {
|
|
1992
|
-
const atom = typeof
|
|
1993
|
-
const cb = typeof
|
|
2069
|
+
const atom = typeof arg3 === "function" ? getAtom(thing, arg2) : getAtom(thing);
|
|
2070
|
+
const cb = typeof arg3 === "function" ? arg3 : arg2;
|
|
1994
2071
|
const listenersKey = `${hook}Listeners`;
|
|
1995
2072
|
if (atom[listenersKey]) {
|
|
1996
2073
|
atom[listenersKey].add(cb);
|
|
@@ -2013,7 +2090,7 @@ function interceptHook(hook, thing, arg2, arg3) {
|
|
|
2013
2090
|
}
|
|
2014
2091
|
|
|
2015
2092
|
function configure(options) {
|
|
2016
|
-
const { enforceActions, computedRequiresReaction, disableErrorBoundaries, reactionScheduler } = options;
|
|
2093
|
+
const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, reactionScheduler, reactionRequiresObservable, observableRequiresReaction } = options;
|
|
2017
2094
|
if (options.isolateGlobalState === true) {
|
|
2018
2095
|
isolateGlobalState();
|
|
2019
2096
|
}
|
|
@@ -2043,6 +2120,16 @@ function configure(options) {
|
|
|
2043
2120
|
if (computedRequiresReaction !== undefined) {
|
|
2044
2121
|
globalState.computedRequiresReaction = !!computedRequiresReaction;
|
|
2045
2122
|
}
|
|
2123
|
+
if (reactionRequiresObservable !== undefined) {
|
|
2124
|
+
globalState.reactionRequiresObservable = !!reactionRequiresObservable;
|
|
2125
|
+
}
|
|
2126
|
+
if (observableRequiresReaction !== undefined) {
|
|
2127
|
+
globalState.observableRequiresReaction = !!observableRequiresReaction;
|
|
2128
|
+
globalState.allowStateReads = !globalState.observableRequiresReaction;
|
|
2129
|
+
}
|
|
2130
|
+
if (computedConfigurable !== undefined) {
|
|
2131
|
+
globalState.computedConfigurable = !!computedConfigurable;
|
|
2132
|
+
}
|
|
2046
2133
|
if (disableErrorBoundaries !== undefined) {
|
|
2047
2134
|
if (disableErrorBoundaries === true)
|
|
2048
2135
|
console.warn("WARNING: Debug feature only. MobX will NOT recover from errors when `disableErrorBoundaries` is enabled.");
|
|
@@ -2094,8 +2181,7 @@ function extendObservableObjectWithProperties(target, properties, decorators, de
|
|
|
2094
2181
|
invariant(!isObservable(properties), "Extending an object with another observable (object) is not supported. Please construct an explicit propertymap, using `toJS` if need. See issue #540");
|
|
2095
2182
|
if (decorators) {
|
|
2096
2183
|
const keys = getPlainObjectKeys(decorators);
|
|
2097
|
-
for (
|
|
2098
|
-
const key = keys[i];
|
|
2184
|
+
for (const key of keys) {
|
|
2099
2185
|
if (!(key in properties))
|
|
2100
2186
|
fail(`Trying to declare a decorator for unspecified property '${stringifyKey(key)}'`);
|
|
2101
2187
|
}
|
|
@@ -2104,10 +2190,11 @@ function extendObservableObjectWithProperties(target, properties, decorators, de
|
|
|
2104
2190
|
startBatch();
|
|
2105
2191
|
try {
|
|
2106
2192
|
const keys = getPlainObjectKeys(properties);
|
|
2107
|
-
for (
|
|
2108
|
-
const key = keys[i];
|
|
2193
|
+
for (const key of keys) {
|
|
2109
2194
|
const descriptor = Object.getOwnPropertyDescriptor(properties, key);
|
|
2110
2195
|
if (process.env.NODE_ENV !== "production") {
|
|
2196
|
+
if (!isPlainObject(properties))
|
|
2197
|
+
fail(`'extendObservabe' only accepts plain objects as second argument`);
|
|
2111
2198
|
if (Object.getOwnPropertyDescriptor(target, key))
|
|
2112
2199
|
fail(`'extendObservable' can only be used to introduce new properties. Use 'set' or 'decorate' instead. The property '${stringifyKey(key)}' already exists on '${target}'`);
|
|
2113
2200
|
if (isComputed(descriptor.value))
|
|
@@ -2813,17 +2900,18 @@ const arrayTraps = {
|
|
|
2813
2900
|
set(target, name, value) {
|
|
2814
2901
|
if (name === "length") {
|
|
2815
2902
|
target[$mobx].setArrayLength(value);
|
|
2816
|
-
return true;
|
|
2817
2903
|
}
|
|
2818
2904
|
if (typeof name === "number") {
|
|
2819
2905
|
arrayExtensions.set.call(target, name, value);
|
|
2820
|
-
return true;
|
|
2821
2906
|
}
|
|
2822
|
-
if (
|
|
2907
|
+
if (typeof name === "symbol" || isNaN(name)) {
|
|
2908
|
+
target[name] = value;
|
|
2909
|
+
}
|
|
2910
|
+
else {
|
|
2911
|
+
// numeric string
|
|
2823
2912
|
arrayExtensions.set.call(target, parseInt(name), value);
|
|
2824
|
-
return true;
|
|
2825
2913
|
}
|
|
2826
|
-
return
|
|
2914
|
+
return true;
|
|
2827
2915
|
},
|
|
2828
2916
|
preventExtensions(target) {
|
|
2829
2917
|
fail(`Observable arrays cannot be frozen`);
|
|
@@ -3069,7 +3157,7 @@ const arrayExtensions = {
|
|
|
3069
3157
|
// which makes it both a 'derivation' and a 'mutation'.
|
|
3070
3158
|
// so we deviate from the default and just make it an dervitation
|
|
3071
3159
|
if (process.env.NODE_ENV !== "production") {
|
|
3072
|
-
console.warn("[mobx] `observableArray.reverse()` will not update the array in place. Use `observableArray.slice().reverse()` to
|
|
3160
|
+
console.warn("[mobx] `observableArray.reverse()` will not update the array in place. Use `observableArray.slice().reverse()` to suppress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().reverse())` to reverse & update in place");
|
|
3073
3161
|
}
|
|
3074
3162
|
const clone = this.slice();
|
|
3075
3163
|
return clone.reverse.apply(clone, arguments);
|
|
@@ -3078,7 +3166,7 @@ const arrayExtensions = {
|
|
|
3078
3166
|
// sort by default mutates in place before returning the result
|
|
3079
3167
|
// which goes against all good practices. Let's not change the array in place!
|
|
3080
3168
|
if (process.env.NODE_ENV !== "production") {
|
|
3081
|
-
console.warn("[mobx] `observableArray.sort()` will not update the array in place. Use `observableArray.slice().sort()` to
|
|
3169
|
+
console.warn("[mobx] `observableArray.sort()` will not update the array in place. Use `observableArray.slice().sort()` to suppress this warning and perform the operation on a copy, or `observableArray.replace(observableArray.slice().sort())` to sort & update in place");
|
|
3082
3170
|
}
|
|
3083
3171
|
const clone = this.slice();
|
|
3084
3172
|
return clone.sort.apply(clone, arguments);
|
|
@@ -3188,9 +3276,16 @@ class ObservableMap {
|
|
|
3188
3276
|
return this._data.has(key);
|
|
3189
3277
|
}
|
|
3190
3278
|
has(key) {
|
|
3191
|
-
if (
|
|
3192
|
-
return this.
|
|
3193
|
-
|
|
3279
|
+
if (!globalState.trackingDerivation)
|
|
3280
|
+
return this._has(key);
|
|
3281
|
+
let entry = this._hasMap.get(key);
|
|
3282
|
+
if (!entry) {
|
|
3283
|
+
// todo: replace with atom (breaking change)
|
|
3284
|
+
const newEntry = (entry = new ObservableValue(this._has(key), referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false));
|
|
3285
|
+
this._hasMap.set(key, newEntry);
|
|
3286
|
+
onBecomeUnobserved(newEntry, () => this._hasMap.delete(key));
|
|
3287
|
+
}
|
|
3288
|
+
return entry.get();
|
|
3194
3289
|
}
|
|
3195
3290
|
set(key, value) {
|
|
3196
3291
|
const hasKey = this._has(key);
|
|
@@ -3252,16 +3347,10 @@ class ObservableMap {
|
|
|
3252
3347
|
return false;
|
|
3253
3348
|
}
|
|
3254
3349
|
_updateHasMapEntry(key, value) {
|
|
3255
|
-
// optimization; don't fill the hasMap if we are not observing, or remove entry if there are no observers anymore
|
|
3256
3350
|
let entry = this._hasMap.get(key);
|
|
3257
3351
|
if (entry) {
|
|
3258
3352
|
entry.setNewValue(value);
|
|
3259
3353
|
}
|
|
3260
|
-
else {
|
|
3261
|
-
entry = new ObservableValue(value, referenceEnhancer, `${this.name}.${stringifyKey(key)}?`, false);
|
|
3262
|
-
this._hasMap.set(key, entry);
|
|
3263
|
-
}
|
|
3264
|
-
return entry;
|
|
3265
3354
|
}
|
|
3266
3355
|
_updateValue(key, newValue) {
|
|
3267
3356
|
const observable = this._data.get(key);
|
|
@@ -3896,7 +3985,7 @@ function getAdministrationForComputedPropOwner(owner) {
|
|
|
3896
3985
|
function generateComputedPropConfig(propName) {
|
|
3897
3986
|
return (computedPropertyConfigs[propName] ||
|
|
3898
3987
|
(computedPropertyConfigs[propName] = {
|
|
3899
|
-
configurable:
|
|
3988
|
+
configurable: globalState.computedConfigurable,
|
|
3900
3989
|
enumerable: false,
|
|
3901
3990
|
get() {
|
|
3902
3991
|
return getAdministrationForComputedPropOwner(this).read(propName);
|
|
@@ -4122,17 +4211,17 @@ function has$1(a, key) {
|
|
|
4122
4211
|
}
|
|
4123
4212
|
|
|
4124
4213
|
function makeIterable(iterator) {
|
|
4125
|
-
iterator[Symbol.iterator] =
|
|
4214
|
+
iterator[Symbol.iterator] = getSelf;
|
|
4126
4215
|
return iterator;
|
|
4127
4216
|
}
|
|
4128
|
-
function
|
|
4217
|
+
function getSelf() {
|
|
4129
4218
|
return this;
|
|
4130
4219
|
}
|
|
4131
4220
|
|
|
4132
4221
|
/*
|
|
4133
4222
|
The only reason for this file to exist is pure horror:
|
|
4134
4223
|
Without it rollup can make the bundling fail at any point in time; when it rolls up the files in the wrong order
|
|
4135
|
-
it will cause undefined errors (for example because super classes or local variables not being
|
|
4224
|
+
it will cause undefined errors (for example because super classes or local variables not being hoisted).
|
|
4136
4225
|
With this file that will still happen,
|
|
4137
4226
|
but at least in this file we can magically reorder the imports with trial and error until the build succeeds again.
|
|
4138
4227
|
*/
|
|
@@ -4164,7 +4253,7 @@ try {
|
|
|
4164
4253
|
process.env.NODE_ENV;
|
|
4165
4254
|
}
|
|
4166
4255
|
catch (e) {
|
|
4167
|
-
const g =
|
|
4256
|
+
const g = getGlobal();
|
|
4168
4257
|
if (typeof process === "undefined")
|
|
4169
4258
|
g.process = {};
|
|
4170
4259
|
g.process.env = {};
|
|
@@ -4190,4 +4279,4 @@ if (typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__ === "object") {
|
|
|
4190
4279
|
});
|
|
4191
4280
|
}
|
|
4192
4281
|
|
|
4193
|
-
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, 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 };
|
|
4282
|
+
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, 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 };
|