mobx 7.0.0 → 7.0.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.
@@ -259,7 +259,8 @@ class Atom {
259
259
  constructor(name_ = "Atom@" + getNextId() ) {
260
260
  this.name_ = void 0;
261
261
  this.flags_ = 0b000;
262
- this.observers_ = new Set();
262
+ // Allocated lazily on first observer to save memory.
263
+ this.observers_ = null;
263
264
  this.lastAccessedBy_ = 0;
264
265
  this.lowestObserverState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
265
266
  // onBecomeObservedListeners
@@ -341,6 +342,10 @@ function compareShallow(a, b) {
341
342
  const compareDefault = Object.is;
342
343
 
343
344
  function deepEnhancer(v, _, name) {
345
+ // primitives can never be made observable; skip the type checks below
346
+ if (v === null || typeof v !== "object" && typeof v !== "function") {
347
+ return v;
348
+ }
344
349
  // it is an observable already, done
345
350
  if (isObservable(v)) {
346
351
  return v;
@@ -1281,7 +1286,8 @@ class ComputedValue {
1281
1286
  // nodes we are looking at. Our value depends on these nodes
1282
1287
  this.newObserving_ = null;
1283
1288
  // during tracking it's an array with new observed observers
1284
- this.observers_ = new Set();
1289
+ // Lazily allocated on first observer - see Atom.observers_.
1290
+ this.observers_ = null;
1285
1291
  this.runId_ = 0;
1286
1292
  this.lastAccessedBy_ = 0;
1287
1293
  this.lowestObserverState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
@@ -1364,9 +1370,9 @@ class ComputedValue {
1364
1370
  if (this.isComputing) {
1365
1371
  die(32, this.name_, this.derivation);
1366
1372
  }
1367
- if (globalState.inBatch === 0 &&
1373
+ if (globalState.inBatch === 0 && (
1368
1374
  // !globalState.trackingDerivatpion &&
1369
- this.observers_.size === 0 && !this.keepAlive_) {
1375
+ !this.observers_ || this.observers_.size === 0) && !this.keepAlive_) {
1370
1376
  if (shouldCompute(this)) {
1371
1377
  this.warnAboutUntrackedRead_();
1372
1378
  startBatch(); // See perf test 'computed memoization'
@@ -1374,6 +1380,7 @@ class ComputedValue {
1374
1380
  endBatch();
1375
1381
  }
1376
1382
  } else {
1383
+ const wasBeingObserved = this.isBeingObserved;
1377
1384
  reportObserved(this);
1378
1385
  if (shouldCompute(this)) {
1379
1386
  let prevTrackingContext = globalState.trackingContext;
@@ -1384,6 +1391,10 @@ class ComputedValue {
1384
1391
  propagateChangeConfirmed(this);
1385
1392
  }
1386
1393
  globalState.trackingContext = prevTrackingContext;
1394
+ } else if (!wasBeingObserved && this.isBeingObserved) {
1395
+ // We just became observed while serving a cached value, so the getter
1396
+ // won't run and won't re-report our dependencies. Cascade to them. #4547
1397
+ this.observing_.forEach(markObserved);
1387
1398
  }
1388
1399
  }
1389
1400
  const result = this.value_;
@@ -1552,7 +1563,7 @@ function isComputingDerivation() {
1552
1563
  return globalState.trackingDerivation !== null; // filter out actions inside computations
1553
1564
  }
1554
1565
  function checkIfStateModificationsAreAllowed(atom) {
1555
- const hasObservers = atom.observers_.size > 0;
1566
+ const hasObservers = !!atom.observers_ && atom.observers_.size > 0;
1556
1567
  // Should not be possible to change observed state outside strict mode, except during initialization, see #563
1557
1568
  if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "always")) {
1558
1569
  console.warn("[MobX] " + (globalState.enforceActions ? "Since strict-mode is enabled, changing (observed) observable values without using an action is not allowed. Tried to modify: " : "Side effects like changing state are not allowed at this point. Are you trying to modify state from, for example, a computed value or the render function of a React component? You can wrap side effects in 'runInAction' (or decorate functions with 'action') if needed. Tried to modify: ") + atom.name_);
@@ -1775,6 +1786,13 @@ class MobXGlobals {
1775
1786
  * Are we currently processing reactions?
1776
1787
  */
1777
1788
  this.isRunningReactions = false;
1789
+ /**
1790
+ * Are we currently draining pendingUnobservations in endBatch?
1791
+ * An onBecomeUnobserved handler can dispose a Reaction, which calls
1792
+ * startBatch/endBatch again; this guards against re-entering the same
1793
+ * drain loop recursively (see endBatch in observable.ts).
1794
+ */
1795
+ this.isRunningUnobservations = false;
1778
1796
  /**
1779
1797
  * Is it allowed to change observables at this point?
1780
1798
  * In general, MobX doesn't allow that when running computations and React.render.
@@ -1891,10 +1909,11 @@ function resetGlobalState() {
1891
1909
  }
1892
1910
 
1893
1911
  function hasObservers(observable) {
1894
- return observable.observers_ && observable.observers_.size > 0;
1912
+ return !!observable.observers_ && observable.observers_.size > 0;
1895
1913
  }
1896
1914
  function getObservers(observable) {
1897
- return observable.observers_;
1915
+ var _observable$observers;
1916
+ return (_observable$observers = observable.observers_) != null ? _observable$observers : new Set();
1898
1917
  }
1899
1918
  // function invariantObservers(observable: IObservable) {
1900
1919
  // const list = observable.observers
@@ -1914,10 +1933,8 @@ function getObservers(observable) {
1914
1933
  // )
1915
1934
  // }
1916
1935
  function addObserver(observable, node) {
1917
- // invariant(node.dependenciesState !== -1, "INTERNAL ERROR, can add only dependenciesState !== -1");
1918
- // invariant(observable._observers.indexOf(node) === -1, "INTERNAL ERROR add already added node");
1919
- // invariantObservers(observable);
1920
- observable.observers_.add(node);
1936
+ var _observable$observers2;
1937
+ ((_observable$observers2 = observable.observers_) != null ? _observable$observers2 : observable.observers_ = new Set()).add(node);
1921
1938
  if (observable.lowestObserverState_ > node.dependenciesState_) {
1922
1939
  observable.lowestObserverState_ = node.dependenciesState_;
1923
1940
  }
@@ -1928,8 +1945,12 @@ function removeObserver(observable, node) {
1928
1945
  // invariant(globalState.inBatch > 0, "INTERNAL ERROR, remove should be called only inside batch");
1929
1946
  // invariant(observable._observers.indexOf(node) !== -1, "INTERNAL ERROR remove already removed node");
1930
1947
  // invariantObservers(observable);
1931
- observable.observers_.delete(node);
1932
- if (observable.observers_.size === 0) {
1948
+ const observers = observable.observers_;
1949
+ if (!observers) {
1950
+ return;
1951
+ }
1952
+ observers.delete(node);
1953
+ if (observers.size === 0) {
1933
1954
  // deleting last observer
1934
1955
  queueForUnobservation(observable);
1935
1956
  }
@@ -1955,26 +1976,59 @@ function endBatch() {
1955
1976
  if (--globalState.inBatch === 0) {
1956
1977
  runReactions();
1957
1978
  // the batch is actually about to finish, all unobserving should happen here.
1958
- const list = globalState.pendingUnobservations;
1959
- for (let i = 0; i < list.length; i++) {
1960
- const observable = list[i];
1961
- observable.isPendingUnobservation = false;
1962
- if (observable.observers_.size === 0) {
1963
- if (observable.isBeingObserved) {
1964
- // if this observable had reactive observers, trigger the hooks
1965
- observable.isBeingObserved = false;
1966
- observable.onBUO();
1967
- }
1968
- if (observable instanceof ComputedValue) {
1969
- // computed values are automatically teared down when the last observer leaves
1970
- // this process happens recursively, this computed might be the last observabe of another, etc..
1971
- observable.suspend_();
1979
+ // Guard against re-entering this loop: an onBUO handler can dispose a Reaction,
1980
+ // which calls startBatch/endBatch again while we're still iterating. Bail out of
1981
+ // the nested call instead of recursing; the outer loop re-reads list.length on
1982
+ // every iteration, so it picks up anything the nested dispose() pushes onto the
1983
+ // same pendingUnobservations array.
1984
+ if (!globalState.isRunningUnobservations) {
1985
+ globalState.isRunningUnobservations = true;
1986
+ try {
1987
+ const list = globalState.pendingUnobservations;
1988
+ for (let i = 0; i < list.length; i++) {
1989
+ const observable = list[i];
1990
+ observable.isPendingUnobservation = false;
1991
+ if (!observable.observers_ || observable.observers_.size === 0) {
1992
+ if (observable.isBeingObserved) {
1993
+ // if this observable had reactive observers, trigger the hooks
1994
+ observable.isBeingObserved = false;
1995
+ observable.onBUO();
1996
+ }
1997
+ if (observable instanceof ComputedValue) {
1998
+ // computed values are automatically teared down when the last observer leaves
1999
+ // this process happens recursively, this computed might be the last observabe of another, etc..
2000
+ observable.suspend_();
2001
+ }
2002
+ }
1972
2003
  }
2004
+ globalState.pendingUnobservations = [];
2005
+ } finally {
2006
+ // Always release the guard, even if an onBUO handler (user code) threw,
2007
+ // otherwise every future endBatch() would see isRunningUnobservations
2008
+ // stuck true and silently stop draining pendingUnobservations forever.
2009
+ globalState.isRunningUnobservations = false;
1973
2010
  }
1974
2011
  }
1975
- globalState.pendingUnobservations = [];
1976
2012
  }
1977
2013
  }
2014
+ /**
2015
+ * Marks an observable as observed, cascading into the dependencies of a ComputedValue.
2016
+ * Unobservation already cascades (`suspend_` -> `clearObserving`), observation normally
2017
+ * only does so by accident: a newly observed computed usually recomputes and re-reports
2018
+ * its dependencies. When it serves a cached value instead nothing re-reports them, so the
2019
+ * transition has to be propagated by hand. See #4547.
2020
+ */
2021
+ function markObserved(observable) {
2022
+ var _observable$observing;
2023
+ if (observable.isBeingObserved) {
2024
+ return;
2025
+ }
2026
+ observable.isBeingObserved = true;
2027
+ observable.onBO();
2028
+ // No queueForUnobservation here: the observer links already exist, so the regular
2029
+ // suspend_ -> clearObserving -> removeObserver teardown still delivers the onBUO.
2030
+ (_observable$observing = observable.observing_) == null || _observable$observing.forEach(markObserved);
2031
+ }
1978
2032
  function reportObserved(observable) {
1979
2033
  checkIfStateReadsAreAllowed(observable);
1980
2034
  const derivation = globalState.trackingDerivation;
@@ -1994,7 +2048,7 @@ function reportObserved(observable) {
1994
2048
  }
1995
2049
  }
1996
2050
  return observable.isBeingObserved;
1997
- } else if (observable.observers_.size === 0 && globalState.inBatch > 0) {
2051
+ } else if ((!observable.observers_ || observable.observers_.size === 0) && globalState.inBatch > 0) {
1998
2052
  queueForUnobservation(observable);
1999
2053
  }
2000
2054
  return false;
@@ -2021,13 +2075,14 @@ function reportObserved(observable) {
2021
2075
  */
2022
2076
  // Called by Atom when its value changes
2023
2077
  function propagateChanged(observable) {
2078
+ var _observable$observers3;
2024
2079
  // invariantLOS(observable, "changed start");
2025
2080
  if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
2026
2081
  return;
2027
2082
  }
2028
2083
  observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
2029
2084
  // Ideally we use for..of here, but the downcompiled version is really slow...
2030
- observable.observers_.forEach(d => {
2085
+ (_observable$observers3 = observable.observers_) == null || _observable$observers3.forEach(d => {
2031
2086
  if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
2032
2087
  d.onBecomeStale_();
2033
2088
  }
@@ -2037,12 +2092,13 @@ function propagateChanged(observable) {
2037
2092
  }
2038
2093
  // Called by ComputedValue when it recalculate and its value changed
2039
2094
  function propagateChangeConfirmed(observable) {
2095
+ var _observable$observers4;
2040
2096
  // invariantLOS(observable, "confirmed start");
2041
2097
  if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
2042
2098
  return;
2043
2099
  }
2044
2100
  observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
2045
- observable.observers_.forEach(d => {
2101
+ (_observable$observers4 = observable.observers_) == null || _observable$observers4.forEach(d => {
2046
2102
  if (d.dependenciesState_ === 1 /* IDerivationState_.POSSIBLY_STALE_ */) {
2047
2103
  d.dependenciesState_ = 2 /* IDerivationState_.STALE_ */;
2048
2104
  } else if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */ // this happens during computing of `d`, just keep lowestObserverState up to date.
@@ -2054,12 +2110,13 @@ function propagateChangeConfirmed(observable) {
2054
2110
  }
2055
2111
  // Used by computed when its dependency changed, but we don't wan't to immediately recompute.
2056
2112
  function propagateMaybeChanged(observable) {
2113
+ var _observable$observers5;
2057
2114
  // invariantLOS(observable, "maybe start");
2058
2115
  if (observable.lowestObserverState_ !== 0 /* IDerivationState_.UP_TO_DATE_ */) {
2059
2116
  return;
2060
2117
  }
2061
2118
  observable.lowestObserverState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
2062
- observable.observers_.forEach(d => {
2119
+ (_observable$observers5 = observable.observers_) == null || _observable$observers5.forEach(d => {
2063
2120
  if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
2064
2121
  d.dependenciesState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
2065
2122
  d.onBecomeStale_();
@@ -4314,31 +4371,16 @@ class ObservableSet {
4314
4371
  });
4315
4372
  }
4316
4373
  intersection(otherSet) {
4317
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4318
- return otherSet.intersection(this);
4319
- } else {
4320
- const dehancedSet = new Set(this);
4321
- return dehancedSet.intersection(otherSet);
4322
- }
4374
+ return new Set(this).intersection(otherSet);
4323
4375
  }
4324
4376
  union(otherSet) {
4325
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4326
- return otherSet.union(this);
4327
- } else {
4328
- const dehancedSet = new Set(this);
4329
- return dehancedSet.union(otherSet);
4330
- }
4377
+ return new Set(this).union(otherSet);
4331
4378
  }
4332
4379
  difference(otherSet) {
4333
4380
  return new Set(this).difference(otherSet);
4334
4381
  }
4335
4382
  symmetricDifference(otherSet) {
4336
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4337
- return otherSet.symmetricDifference(this);
4338
- } else {
4339
- const dehancedSet = new Set(this);
4340
- return dehancedSet.symmetricDifference(otherSet);
4341
- }
4383
+ return new Set(this).symmetricDifference(otherSet);
4342
4384
  }
4343
4385
  isSubsetOf(otherSet) {
4344
4386
  return new Set(this).isSubsetOf(otherSet);
@@ -4347,12 +4389,7 @@ class ObservableSet {
4347
4389
  return new Set(this).isSupersetOf(otherSet);
4348
4390
  }
4349
4391
  isDisjointFrom(otherSet) {
4350
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4351
- return otherSet.isDisjointFrom(this);
4352
- } else {
4353
- const dehancedSet = new Set(this);
4354
- return dehancedSet.isDisjointFrom(otherSet);
4355
- }
4392
+ return new Set(this).isDisjointFrom(otherSet);
4356
4393
  }
4357
4394
  replace(other) {
4358
4395
  if (isObservableSet(other)) {