mobx 5.13.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 +6 -1
- package/LICENSE +0 -0
- package/README.md +0 -0
- 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 +10 -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 +0 -0
- 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 +1 -0
- package/lib/core/atom.d.ts +0 -0
- package/lib/core/computedvalue.d.ts +0 -0
- package/lib/core/derivation.d.ts +7 -0
- package/lib/core/globalstate.d.ts +15 -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 +0 -0
- package/lib/mobx.es6.js +303 -246
- package/lib/mobx.js +304 -246
- package/lib/mobx.js.flow +18 -0
- package/lib/mobx.min.js +1 -1
- package/lib/mobx.module.js +305 -247
- package/lib/mobx.umd.js +304 -246
- 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 +1 -1
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");
|
|
@@ -557,9 +825,11 @@ function _startAction(actionName, scope, args) {
|
|
|
557
825
|
const prevDerivation = untrackedStart();
|
|
558
826
|
startBatch();
|
|
559
827
|
const prevAllowStateChanges = allowStateChangesStart(true);
|
|
828
|
+
const prevAllowStateReads = allowStateReadsStart(true);
|
|
560
829
|
const runInfo = {
|
|
561
830
|
prevDerivation,
|
|
562
831
|
prevAllowStateChanges,
|
|
832
|
+
prevAllowStateReads,
|
|
563
833
|
notifySpy,
|
|
564
834
|
startTime,
|
|
565
835
|
actionId: globalState.nextActionId++,
|
|
@@ -577,6 +847,7 @@ function _endAction(runInfo) {
|
|
|
577
847
|
globalState.suppressReactionErrors = true;
|
|
578
848
|
}
|
|
579
849
|
allowStateChangesEnd(runInfo.prevAllowStateChanges);
|
|
850
|
+
allowStateReadsEnd(runInfo.prevAllowStateReads);
|
|
580
851
|
endBatch();
|
|
581
852
|
untrackedEnd(runInfo.prevDerivation);
|
|
582
853
|
if (runInfo.notifySpy && process.env.NODE_ENV !== "production") {
|
|
@@ -935,247 +1206,6 @@ class ComputedValue {
|
|
|
935
1206
|
}
|
|
936
1207
|
const isComputedValue = createInstanceofPredicate("ComputedValue", ComputedValue);
|
|
937
1208
|
|
|
938
|
-
var IDerivationState;
|
|
939
|
-
(function (IDerivationState) {
|
|
940
|
-
// before being run or (outside batch and not being observed)
|
|
941
|
-
// at this point derivation is not holding any data about dependency tree
|
|
942
|
-
IDerivationState[IDerivationState["NOT_TRACKING"] = -1] = "NOT_TRACKING";
|
|
943
|
-
// no shallow dependency changed since last computation
|
|
944
|
-
// won't recalculate derivation
|
|
945
|
-
// this is what makes mobx fast
|
|
946
|
-
IDerivationState[IDerivationState["UP_TO_DATE"] = 0] = "UP_TO_DATE";
|
|
947
|
-
// some deep dependency changed, but don't know if shallow dependency changed
|
|
948
|
-
// will require to check first if UP_TO_DATE or POSSIBLY_STALE
|
|
949
|
-
// currently only ComputedValue will propagate POSSIBLY_STALE
|
|
950
|
-
//
|
|
951
|
-
// having this state is second big optimization:
|
|
952
|
-
// don't have to recompute on every dependency change, but only when it's needed
|
|
953
|
-
IDerivationState[IDerivationState["POSSIBLY_STALE"] = 1] = "POSSIBLY_STALE";
|
|
954
|
-
// A shallow dependency has changed since last computation and the derivation
|
|
955
|
-
// will need to recompute when it's needed next.
|
|
956
|
-
IDerivationState[IDerivationState["STALE"] = 2] = "STALE";
|
|
957
|
-
})(IDerivationState || (IDerivationState = {}));
|
|
958
|
-
var TraceMode;
|
|
959
|
-
(function (TraceMode) {
|
|
960
|
-
TraceMode[TraceMode["NONE"] = 0] = "NONE";
|
|
961
|
-
TraceMode[TraceMode["LOG"] = 1] = "LOG";
|
|
962
|
-
TraceMode[TraceMode["BREAK"] = 2] = "BREAK";
|
|
963
|
-
})(TraceMode || (TraceMode = {}));
|
|
964
|
-
class CaughtException {
|
|
965
|
-
constructor(cause) {
|
|
966
|
-
this.cause = cause;
|
|
967
|
-
// Empty
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
function isCaughtException(e) {
|
|
971
|
-
return e instanceof CaughtException;
|
|
972
|
-
}
|
|
973
|
-
/**
|
|
974
|
-
* Finds out whether any dependency of the derivation has actually changed.
|
|
975
|
-
* If dependenciesState is 1 then it will recalculate dependencies,
|
|
976
|
-
* if any dependency changed it will propagate it by changing dependenciesState to 2.
|
|
977
|
-
*
|
|
978
|
-
* By iterating over the dependencies in the same order that they were reported and
|
|
979
|
-
* stopping on the first change, all the recalculations are only called for ComputedValues
|
|
980
|
-
* that will be tracked by derivation. That is because we assume that if the first x
|
|
981
|
-
* dependencies of the derivation doesn't change then the derivation should run the same way
|
|
982
|
-
* up until accessing x-th dependency.
|
|
983
|
-
*/
|
|
984
|
-
function shouldCompute(derivation) {
|
|
985
|
-
switch (derivation.dependenciesState) {
|
|
986
|
-
case IDerivationState.UP_TO_DATE:
|
|
987
|
-
return false;
|
|
988
|
-
case IDerivationState.NOT_TRACKING:
|
|
989
|
-
case IDerivationState.STALE:
|
|
990
|
-
return true;
|
|
991
|
-
case IDerivationState.POSSIBLY_STALE: {
|
|
992
|
-
const prevUntracked = untrackedStart(); // no need for those computeds to be reported, they will be picked up in trackDerivedFunction.
|
|
993
|
-
const obs = derivation.observing, l = obs.length;
|
|
994
|
-
for (let i = 0; i < l; i++) {
|
|
995
|
-
const obj = obs[i];
|
|
996
|
-
if (isComputedValue(obj)) {
|
|
997
|
-
if (globalState.disableErrorBoundaries) {
|
|
998
|
-
obj.get();
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
try {
|
|
1002
|
-
obj.get();
|
|
1003
|
-
}
|
|
1004
|
-
catch (e) {
|
|
1005
|
-
// we are not interested in the value *or* exception at this moment, but if there is one, notify all
|
|
1006
|
-
untrackedEnd(prevUntracked);
|
|
1007
|
-
return true;
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
// if ComputedValue `obj` actually changed it will be computed and propagated to its observers.
|
|
1011
|
-
// and `derivation` is an observer of `obj`
|
|
1012
|
-
// invariantShouldCompute(derivation)
|
|
1013
|
-
if (derivation.dependenciesState === IDerivationState.STALE) {
|
|
1014
|
-
untrackedEnd(prevUntracked);
|
|
1015
|
-
return true;
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
changeDependenciesStateTo0(derivation);
|
|
1020
|
-
untrackedEnd(prevUntracked);
|
|
1021
|
-
return false;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
// function invariantShouldCompute(derivation: IDerivation) {
|
|
1026
|
-
// const newDepState = (derivation as any).dependenciesState
|
|
1027
|
-
// if (
|
|
1028
|
-
// process.env.NODE_ENV === "production" &&
|
|
1029
|
-
// (newDepState === IDerivationState.POSSIBLY_STALE ||
|
|
1030
|
-
// newDepState === IDerivationState.NOT_TRACKING)
|
|
1031
|
-
// )
|
|
1032
|
-
// fail("Illegal dependency state")
|
|
1033
|
-
// }
|
|
1034
|
-
function isComputingDerivation() {
|
|
1035
|
-
return globalState.trackingDerivation !== null; // filter out actions inside computations
|
|
1036
|
-
}
|
|
1037
|
-
function checkIfStateModificationsAreAllowed(atom) {
|
|
1038
|
-
const hasObservers = atom.observers.size > 0;
|
|
1039
|
-
// Should never be possible to change an observed observable from inside computed, see #798
|
|
1040
|
-
if (globalState.computationDepth > 0 && hasObservers)
|
|
1041
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1042
|
-
`Computed values are not allowed to cause side effects by changing observables that are already being observed. Tried to modify: ${atom.name}`);
|
|
1043
|
-
// Should not be possible to change observed state outside strict mode, except during initialization, see #563
|
|
1044
|
-
if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "strict"))
|
|
1045
|
-
fail(process.env.NODE_ENV !== "production" &&
|
|
1046
|
-
(globalState.enforceActions
|
|
1047
|
-
? "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: "
|
|
1048
|
-
: "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: ") +
|
|
1049
|
-
atom.name);
|
|
1050
|
-
}
|
|
1051
|
-
/**
|
|
1052
|
-
* Executes the provided function `f` and tracks which observables are being accessed.
|
|
1053
|
-
* The tracking information is stored on the `derivation` object and the derivation is registered
|
|
1054
|
-
* as observer of any of the accessed observables.
|
|
1055
|
-
*/
|
|
1056
|
-
function trackDerivedFunction(derivation, f, context) {
|
|
1057
|
-
// pre allocate array allocation + room for variation in deps
|
|
1058
|
-
// array will be trimmed by bindDependencies
|
|
1059
|
-
changeDependenciesStateTo0(derivation);
|
|
1060
|
-
derivation.newObserving = new Array(derivation.observing.length + 100);
|
|
1061
|
-
derivation.unboundDepsCount = 0;
|
|
1062
|
-
derivation.runId = ++globalState.runId;
|
|
1063
|
-
const prevTracking = globalState.trackingDerivation;
|
|
1064
|
-
globalState.trackingDerivation = derivation;
|
|
1065
|
-
let result;
|
|
1066
|
-
if (globalState.disableErrorBoundaries === true) {
|
|
1067
|
-
result = f.call(context);
|
|
1068
|
-
}
|
|
1069
|
-
else {
|
|
1070
|
-
try {
|
|
1071
|
-
result = f.call(context);
|
|
1072
|
-
}
|
|
1073
|
-
catch (e) {
|
|
1074
|
-
result = new CaughtException(e);
|
|
1075
|
-
}
|
|
1076
|
-
}
|
|
1077
|
-
globalState.trackingDerivation = prevTracking;
|
|
1078
|
-
bindDependencies(derivation);
|
|
1079
|
-
return result;
|
|
1080
|
-
}
|
|
1081
|
-
/**
|
|
1082
|
-
* diffs newObserving with observing.
|
|
1083
|
-
* update observing to be newObserving with unique observables
|
|
1084
|
-
* notify observers that become observed/unobserved
|
|
1085
|
-
*/
|
|
1086
|
-
function bindDependencies(derivation) {
|
|
1087
|
-
// invariant(derivation.dependenciesState !== IDerivationState.NOT_TRACKING, "INTERNAL ERROR bindDependencies expects derivation.dependenciesState !== -1");
|
|
1088
|
-
const prevObserving = derivation.observing;
|
|
1089
|
-
const observing = (derivation.observing = derivation.newObserving);
|
|
1090
|
-
let lowestNewObservingDerivationState = IDerivationState.UP_TO_DATE;
|
|
1091
|
-
// Go through all new observables and check diffValue: (this list can contain duplicates):
|
|
1092
|
-
// 0: first occurrence, change to 1 and keep it
|
|
1093
|
-
// 1: extra occurrence, drop it
|
|
1094
|
-
let i0 = 0, l = derivation.unboundDepsCount;
|
|
1095
|
-
for (let i = 0; i < l; i++) {
|
|
1096
|
-
const dep = observing[i];
|
|
1097
|
-
if (dep.diffValue === 0) {
|
|
1098
|
-
dep.diffValue = 1;
|
|
1099
|
-
if (i0 !== i)
|
|
1100
|
-
observing[i0] = dep;
|
|
1101
|
-
i0++;
|
|
1102
|
-
}
|
|
1103
|
-
// Upcast is 'safe' here, because if dep is IObservable, `dependenciesState` will be undefined,
|
|
1104
|
-
// not hitting the condition
|
|
1105
|
-
if (dep.dependenciesState > lowestNewObservingDerivationState) {
|
|
1106
|
-
lowestNewObservingDerivationState = dep.dependenciesState;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
observing.length = i0;
|
|
1110
|
-
derivation.newObserving = null; // newObserving shouldn't be needed outside tracking (statement moved down to work around FF bug, see #614)
|
|
1111
|
-
// Go through all old observables and check diffValue: (it is unique after last bindDependencies)
|
|
1112
|
-
// 0: it's not in new observables, unobserve it
|
|
1113
|
-
// 1: it keeps being observed, don't want to notify it. change to 0
|
|
1114
|
-
l = prevObserving.length;
|
|
1115
|
-
while (l--) {
|
|
1116
|
-
const dep = prevObserving[l];
|
|
1117
|
-
if (dep.diffValue === 0) {
|
|
1118
|
-
removeObserver(dep, derivation);
|
|
1119
|
-
}
|
|
1120
|
-
dep.diffValue = 0;
|
|
1121
|
-
}
|
|
1122
|
-
// Go through all new observables and check diffValue: (now it should be unique)
|
|
1123
|
-
// 0: it was set to 0 in last loop. don't need to do anything.
|
|
1124
|
-
// 1: it wasn't observed, let's observe it. set back to 0
|
|
1125
|
-
while (i0--) {
|
|
1126
|
-
const dep = observing[i0];
|
|
1127
|
-
if (dep.diffValue === 1) {
|
|
1128
|
-
dep.diffValue = 0;
|
|
1129
|
-
addObserver(dep, derivation);
|
|
1130
|
-
}
|
|
1131
|
-
}
|
|
1132
|
-
// Some new observed derivations may become stale during this derivation computation
|
|
1133
|
-
// so they have had no chance to propagate staleness (#916)
|
|
1134
|
-
if (lowestNewObservingDerivationState !== IDerivationState.UP_TO_DATE) {
|
|
1135
|
-
derivation.dependenciesState = lowestNewObservingDerivationState;
|
|
1136
|
-
derivation.onBecomeStale();
|
|
1137
|
-
}
|
|
1138
|
-
}
|
|
1139
|
-
function clearObserving(derivation) {
|
|
1140
|
-
// invariant(globalState.inBatch > 0, "INTERNAL ERROR clearObserving should be called only inside batch");
|
|
1141
|
-
const obs = derivation.observing;
|
|
1142
|
-
derivation.observing = [];
|
|
1143
|
-
let i = obs.length;
|
|
1144
|
-
while (i--)
|
|
1145
|
-
removeObserver(obs[i], derivation);
|
|
1146
|
-
derivation.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
1147
|
-
}
|
|
1148
|
-
function untracked(action) {
|
|
1149
|
-
const prev = untrackedStart();
|
|
1150
|
-
try {
|
|
1151
|
-
return action();
|
|
1152
|
-
}
|
|
1153
|
-
finally {
|
|
1154
|
-
untrackedEnd(prev);
|
|
1155
|
-
}
|
|
1156
|
-
}
|
|
1157
|
-
function untrackedStart() {
|
|
1158
|
-
const prev = globalState.trackingDerivation;
|
|
1159
|
-
globalState.trackingDerivation = null;
|
|
1160
|
-
return prev;
|
|
1161
|
-
}
|
|
1162
|
-
function untrackedEnd(prev) {
|
|
1163
|
-
globalState.trackingDerivation = prev;
|
|
1164
|
-
}
|
|
1165
|
-
/**
|
|
1166
|
-
* needed to keep `lowestObserverState` correct. when changing from (2 or 1) to 0
|
|
1167
|
-
*
|
|
1168
|
-
*/
|
|
1169
|
-
function changeDependenciesStateTo0(derivation) {
|
|
1170
|
-
if (derivation.dependenciesState === IDerivationState.UP_TO_DATE)
|
|
1171
|
-
return;
|
|
1172
|
-
derivation.dependenciesState = IDerivationState.UP_TO_DATE;
|
|
1173
|
-
const obs = derivation.observing;
|
|
1174
|
-
let i = obs.length;
|
|
1175
|
-
while (i--)
|
|
1176
|
-
obs[i].lowestObserverState = IDerivationState.UP_TO_DATE;
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
1209
|
/**
|
|
1180
1210
|
* These values will persist if global state is reset
|
|
1181
1211
|
*/
|
|
@@ -1184,6 +1214,9 @@ const persistentKeys = [
|
|
|
1184
1214
|
"spyListeners",
|
|
1185
1215
|
"enforceActions",
|
|
1186
1216
|
"computedRequiresReaction",
|
|
1217
|
+
"reactionRequiresObservable",
|
|
1218
|
+
"observableRequiresReaction",
|
|
1219
|
+
"allowStateReads",
|
|
1187
1220
|
"disableErrorBoundaries",
|
|
1188
1221
|
"runId",
|
|
1189
1222
|
"UNCHANGED"
|
|
@@ -1244,6 +1277,11 @@ class MobXGlobals {
|
|
|
1244
1277
|
* To ensure that those functions stay pure.
|
|
1245
1278
|
*/
|
|
1246
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;
|
|
1247
1285
|
/**
|
|
1248
1286
|
* If strict mode is enabled, state changes are by default not allowed
|
|
1249
1287
|
*/
|
|
@@ -1260,6 +1298,16 @@ class MobXGlobals {
|
|
|
1260
1298
|
* Warn if computed values are accessed outside a reactive context
|
|
1261
1299
|
*/
|
|
1262
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;
|
|
1263
1311
|
/**
|
|
1264
1312
|
* Allows overwriting of computed properties, useful in tests but not prod as it can cause
|
|
1265
1313
|
* memory leaks. See https://github.com/mobxjs/mobx/issues/1867
|
|
@@ -1434,6 +1482,7 @@ function endBatch() {
|
|
|
1434
1482
|
}
|
|
1435
1483
|
}
|
|
1436
1484
|
function reportObserved(observable) {
|
|
1485
|
+
checkIfStateReadsAreAllowed(observable);
|
|
1437
1486
|
const derivation = globalState.trackingDerivation;
|
|
1438
1487
|
if (derivation !== null) {
|
|
1439
1488
|
/**
|
|
@@ -1561,10 +1610,11 @@ function printDepTree(tree, lines, depth) {
|
|
|
1561
1610
|
}
|
|
1562
1611
|
|
|
1563
1612
|
class Reaction {
|
|
1564
|
-
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler) {
|
|
1613
|
+
constructor(name = "Reaction@" + getNextId(), onInvalidate, errorHandler, requiresObservable = false) {
|
|
1565
1614
|
this.name = name;
|
|
1566
1615
|
this.onInvalidate = onInvalidate;
|
|
1567
1616
|
this.errorHandler = errorHandler;
|
|
1617
|
+
this.requiresObservable = requiresObservable;
|
|
1568
1618
|
this.observing = []; // nodes we are looking at. Our value depends on these nodes
|
|
1569
1619
|
this.newObserving = [];
|
|
1570
1620
|
this.dependenciesState = IDerivationState.NOT_TRACKING;
|
|
@@ -1923,7 +1973,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1923
1973
|
// normal autorun
|
|
1924
1974
|
reaction = new Reaction(name, function () {
|
|
1925
1975
|
this.track(reactionRunner);
|
|
1926
|
-
}, opts.onError);
|
|
1976
|
+
}, opts.onError, opts.requiresObservable);
|
|
1927
1977
|
}
|
|
1928
1978
|
else {
|
|
1929
1979
|
const scheduler = createSchedulerFromOptions(opts);
|
|
@@ -1938,7 +1988,7 @@ function autorun(view, opts = EMPTY_OBJECT) {
|
|
|
1938
1988
|
reaction.track(reactionRunner);
|
|
1939
1989
|
});
|
|
1940
1990
|
}
|
|
1941
|
-
}, opts.onError);
|
|
1991
|
+
}, opts.onError, opts.requiresObservable);
|
|
1942
1992
|
}
|
|
1943
1993
|
function reactionRunner() {
|
|
1944
1994
|
view(reaction);
|
|
@@ -1977,7 +2027,7 @@ function reaction(expression, effect, opts = EMPTY_OBJECT) {
|
|
|
1977
2027
|
isScheduled = true;
|
|
1978
2028
|
scheduler(reactionRunner);
|
|
1979
2029
|
}
|
|
1980
|
-
}, opts.onError);
|
|
2030
|
+
}, opts.onError, opts.requiresObservable);
|
|
1981
2031
|
function reactionRunner() {
|
|
1982
2032
|
isScheduled = false; // Q: move into reaction runner?
|
|
1983
2033
|
if (r.isDisposed)
|
|
@@ -2040,7 +2090,7 @@ function interceptHook(hook, thing, arg2, arg3) {
|
|
|
2040
2090
|
}
|
|
2041
2091
|
|
|
2042
2092
|
function configure(options) {
|
|
2043
|
-
const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, reactionScheduler } = options;
|
|
2093
|
+
const { enforceActions, computedRequiresReaction, computedConfigurable, disableErrorBoundaries, reactionScheduler, reactionRequiresObservable, observableRequiresReaction } = options;
|
|
2044
2094
|
if (options.isolateGlobalState === true) {
|
|
2045
2095
|
isolateGlobalState();
|
|
2046
2096
|
}
|
|
@@ -2070,6 +2120,13 @@ function configure(options) {
|
|
|
2070
2120
|
if (computedRequiresReaction !== undefined) {
|
|
2071
2121
|
globalState.computedRequiresReaction = !!computedRequiresReaction;
|
|
2072
2122
|
}
|
|
2123
|
+
if (reactionRequiresObservable !== undefined) {
|
|
2124
|
+
globalState.reactionRequiresObservable = !!reactionRequiresObservable;
|
|
2125
|
+
}
|
|
2126
|
+
if (observableRequiresReaction !== undefined) {
|
|
2127
|
+
globalState.observableRequiresReaction = !!observableRequiresReaction;
|
|
2128
|
+
globalState.allowStateReads = !globalState.observableRequiresReaction;
|
|
2129
|
+
}
|
|
2073
2130
|
if (computedConfigurable !== undefined) {
|
|
2074
2131
|
globalState.computedConfigurable = !!computedConfigurable;
|
|
2075
2132
|
}
|