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.
package/dist/mobx.mjs CHANGED
@@ -260,7 +260,8 @@ class Atom {
260
260
  constructor(name_ = process.env.NODE_ENV !== "production" ? "Atom@" + getNextId() : "Atom") {
261
261
  this.name_ = void 0;
262
262
  this.flags_ = 0b000;
263
- this.observers_ = new Set();
263
+ // Allocated lazily on first observer to save memory.
264
+ this.observers_ = null;
264
265
  this.lastAccessedBy_ = 0;
265
266
  this.lowestObserverState_ = -1 /* IDerivationState_.NOT_TRACKING_ */;
266
267
  // onBecomeObservedListeners
@@ -342,6 +343,10 @@ function compareShallow(a, b) {
342
343
  const compareDefault = Object.is;
343
344
 
344
345
  function deepEnhancer(v, _, name) {
346
+ // primitives can never be made observable; skip the type checks below
347
+ if (v === null || typeof v !== "object" && typeof v !== "function") {
348
+ return v;
349
+ }
345
350
  // it is an observable already, done
346
351
  if (isObservable(v)) {
347
352
  return v;
@@ -1282,7 +1287,8 @@ class ComputedValue {
1282
1287
  // nodes we are looking at. Our value depends on these nodes
1283
1288
  this.newObserving_ = null;
1284
1289
  // during tracking it's an array with new observed observers
1285
- this.observers_ = new Set();
1290
+ // Lazily allocated on first observer - see Atom.observers_.
1291
+ this.observers_ = null;
1286
1292
  this.runId_ = 0;
1287
1293
  this.lastAccessedBy_ = 0;
1288
1294
  this.lowestObserverState_ = 0 /* IDerivationState_.UP_TO_DATE_ */;
@@ -1365,9 +1371,9 @@ class ComputedValue {
1365
1371
  if (this.isComputing) {
1366
1372
  die(32, this.name_, this.derivation);
1367
1373
  }
1368
- if (globalState.inBatch === 0 &&
1374
+ if (globalState.inBatch === 0 && (
1369
1375
  // !globalState.trackingDerivatpion &&
1370
- this.observers_.size === 0 && !this.keepAlive_) {
1376
+ !this.observers_ || this.observers_.size === 0) && !this.keepAlive_) {
1371
1377
  if (shouldCompute(this)) {
1372
1378
  this.warnAboutUntrackedRead_();
1373
1379
  startBatch(); // See perf test 'computed memoization'
@@ -1375,6 +1381,7 @@ class ComputedValue {
1375
1381
  endBatch();
1376
1382
  }
1377
1383
  } else {
1384
+ const wasBeingObserved = this.isBeingObserved;
1378
1385
  reportObserved(this);
1379
1386
  if (shouldCompute(this)) {
1380
1387
  let prevTrackingContext = globalState.trackingContext;
@@ -1385,6 +1392,10 @@ class ComputedValue {
1385
1392
  propagateChangeConfirmed(this);
1386
1393
  }
1387
1394
  globalState.trackingContext = prevTrackingContext;
1395
+ } else if (!wasBeingObserved && this.isBeingObserved) {
1396
+ // We just became observed while serving a cached value, so the getter
1397
+ // won't run and won't re-report our dependencies. Cascade to them. #4547
1398
+ this.observing_.forEach(markObserved);
1388
1399
  }
1389
1400
  }
1390
1401
  const result = this.value_;
@@ -1559,7 +1570,7 @@ function checkIfStateModificationsAreAllowed(atom) {
1559
1570
  if (!(process.env.NODE_ENV !== "production")) {
1560
1571
  return;
1561
1572
  }
1562
- const hasObservers = atom.observers_.size > 0;
1573
+ const hasObservers = !!atom.observers_ && atom.observers_.size > 0;
1563
1574
  // Should not be possible to change observed state outside strict mode, except during initialization, see #563
1564
1575
  if (!globalState.allowStateChanges && (hasObservers || globalState.enforceActions === "always")) {
1565
1576
  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_);
@@ -1785,6 +1796,13 @@ class MobXGlobals {
1785
1796
  * Are we currently processing reactions?
1786
1797
  */
1787
1798
  this.isRunningReactions = false;
1799
+ /**
1800
+ * Are we currently draining pendingUnobservations in endBatch?
1801
+ * An onBecomeUnobserved handler can dispose a Reaction, which calls
1802
+ * startBatch/endBatch again; this guards against re-entering the same
1803
+ * drain loop recursively (see endBatch in observable.ts).
1804
+ */
1805
+ this.isRunningUnobservations = false;
1788
1806
  /**
1789
1807
  * Is it allowed to change observables at this point?
1790
1808
  * In general, MobX doesn't allow that when running computations and React.render.
@@ -1901,10 +1919,11 @@ function resetGlobalState() {
1901
1919
  }
1902
1920
 
1903
1921
  function hasObservers(observable) {
1904
- return observable.observers_ && observable.observers_.size > 0;
1922
+ return !!observable.observers_ && observable.observers_.size > 0;
1905
1923
  }
1906
1924
  function getObservers(observable) {
1907
- return observable.observers_;
1925
+ var _observable$observers;
1926
+ return (_observable$observers = observable.observers_) != null ? _observable$observers : new Set();
1908
1927
  }
1909
1928
  // function invariantObservers(observable: IObservable) {
1910
1929
  // const list = observable.observers
@@ -1924,10 +1943,8 @@ function getObservers(observable) {
1924
1943
  // )
1925
1944
  // }
1926
1945
  function addObserver(observable, node) {
1927
- // invariant(node.dependenciesState !== -1, "INTERNAL ERROR, can add only dependenciesState !== -1");
1928
- // invariant(observable._observers.indexOf(node) === -1, "INTERNAL ERROR add already added node");
1929
- // invariantObservers(observable);
1930
- observable.observers_.add(node);
1946
+ var _observable$observers2;
1947
+ ((_observable$observers2 = observable.observers_) != null ? _observable$observers2 : observable.observers_ = new Set()).add(node);
1931
1948
  if (observable.lowestObserverState_ > node.dependenciesState_) {
1932
1949
  observable.lowestObserverState_ = node.dependenciesState_;
1933
1950
  }
@@ -1938,8 +1955,12 @@ function removeObserver(observable, node) {
1938
1955
  // invariant(globalState.inBatch > 0, "INTERNAL ERROR, remove should be called only inside batch");
1939
1956
  // invariant(observable._observers.indexOf(node) !== -1, "INTERNAL ERROR remove already removed node");
1940
1957
  // invariantObservers(observable);
1941
- observable.observers_.delete(node);
1942
- if (observable.observers_.size === 0) {
1958
+ const observers = observable.observers_;
1959
+ if (!observers) {
1960
+ return;
1961
+ }
1962
+ observers.delete(node);
1963
+ if (observers.size === 0) {
1943
1964
  // deleting last observer
1944
1965
  queueForUnobservation(observable);
1945
1966
  }
@@ -1965,26 +1986,59 @@ function endBatch() {
1965
1986
  if (--globalState.inBatch === 0) {
1966
1987
  runReactions();
1967
1988
  // the batch is actually about to finish, all unobserving should happen here.
1968
- const list = globalState.pendingUnobservations;
1969
- for (let i = 0; i < list.length; i++) {
1970
- const observable = list[i];
1971
- observable.isPendingUnobservation = false;
1972
- if (observable.observers_.size === 0) {
1973
- if (observable.isBeingObserved) {
1974
- // if this observable had reactive observers, trigger the hooks
1975
- observable.isBeingObserved = false;
1976
- observable.onBUO();
1977
- }
1978
- if (observable instanceof ComputedValue) {
1979
- // computed values are automatically teared down when the last observer leaves
1980
- // this process happens recursively, this computed might be the last observabe of another, etc..
1981
- observable.suspend_();
1989
+ // Guard against re-entering this loop: an onBUO handler can dispose a Reaction,
1990
+ // which calls startBatch/endBatch again while we're still iterating. Bail out of
1991
+ // the nested call instead of recursing; the outer loop re-reads list.length on
1992
+ // every iteration, so it picks up anything the nested dispose() pushes onto the
1993
+ // same pendingUnobservations array.
1994
+ if (!globalState.isRunningUnobservations) {
1995
+ globalState.isRunningUnobservations = true;
1996
+ try {
1997
+ const list = globalState.pendingUnobservations;
1998
+ for (let i = 0; i < list.length; i++) {
1999
+ const observable = list[i];
2000
+ observable.isPendingUnobservation = false;
2001
+ if (!observable.observers_ || observable.observers_.size === 0) {
2002
+ if (observable.isBeingObserved) {
2003
+ // if this observable had reactive observers, trigger the hooks
2004
+ observable.isBeingObserved = false;
2005
+ observable.onBUO();
2006
+ }
2007
+ if (observable instanceof ComputedValue) {
2008
+ // computed values are automatically teared down when the last observer leaves
2009
+ // this process happens recursively, this computed might be the last observabe of another, etc..
2010
+ observable.suspend_();
2011
+ }
2012
+ }
1982
2013
  }
2014
+ globalState.pendingUnobservations = [];
2015
+ } finally {
2016
+ // Always release the guard, even if an onBUO handler (user code) threw,
2017
+ // otherwise every future endBatch() would see isRunningUnobservations
2018
+ // stuck true and silently stop draining pendingUnobservations forever.
2019
+ globalState.isRunningUnobservations = false;
1983
2020
  }
1984
2021
  }
1985
- globalState.pendingUnobservations = [];
1986
2022
  }
1987
2023
  }
2024
+ /**
2025
+ * Marks an observable as observed, cascading into the dependencies of a ComputedValue.
2026
+ * Unobservation already cascades (`suspend_` -> `clearObserving`), observation normally
2027
+ * only does so by accident: a newly observed computed usually recomputes and re-reports
2028
+ * its dependencies. When it serves a cached value instead nothing re-reports them, so the
2029
+ * transition has to be propagated by hand. See #4547.
2030
+ */
2031
+ function markObserved(observable) {
2032
+ var _observable$observing;
2033
+ if (observable.isBeingObserved) {
2034
+ return;
2035
+ }
2036
+ observable.isBeingObserved = true;
2037
+ observable.onBO();
2038
+ // No queueForUnobservation here: the observer links already exist, so the regular
2039
+ // suspend_ -> clearObserving -> removeObserver teardown still delivers the onBUO.
2040
+ (_observable$observing = observable.observing_) == null || _observable$observing.forEach(markObserved);
2041
+ }
1988
2042
  function reportObserved(observable) {
1989
2043
  checkIfStateReadsAreAllowed(observable);
1990
2044
  const derivation = globalState.trackingDerivation;
@@ -2004,7 +2058,7 @@ function reportObserved(observable) {
2004
2058
  }
2005
2059
  }
2006
2060
  return observable.isBeingObserved;
2007
- } else if (observable.observers_.size === 0 && globalState.inBatch > 0) {
2061
+ } else if ((!observable.observers_ || observable.observers_.size === 0) && globalState.inBatch > 0) {
2008
2062
  queueForUnobservation(observable);
2009
2063
  }
2010
2064
  return false;
@@ -2031,13 +2085,14 @@ function reportObserved(observable) {
2031
2085
  */
2032
2086
  // Called by Atom when its value changes
2033
2087
  function propagateChanged(observable) {
2088
+ var _observable$observers3;
2034
2089
  // invariantLOS(observable, "changed start");
2035
2090
  if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
2036
2091
  return;
2037
2092
  }
2038
2093
  observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
2039
2094
  // Ideally we use for..of here, but the downcompiled version is really slow...
2040
- observable.observers_.forEach(d => {
2095
+ (_observable$observers3 = observable.observers_) == null || _observable$observers3.forEach(d => {
2041
2096
  if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
2042
2097
  d.onBecomeStale_();
2043
2098
  }
@@ -2047,12 +2102,13 @@ function propagateChanged(observable) {
2047
2102
  }
2048
2103
  // Called by ComputedValue when it recalculate and its value changed
2049
2104
  function propagateChangeConfirmed(observable) {
2105
+ var _observable$observers4;
2050
2106
  // invariantLOS(observable, "confirmed start");
2051
2107
  if (observable.lowestObserverState_ === 2 /* IDerivationState_.STALE_ */) {
2052
2108
  return;
2053
2109
  }
2054
2110
  observable.lowestObserverState_ = 2 /* IDerivationState_.STALE_ */;
2055
- observable.observers_.forEach(d => {
2111
+ (_observable$observers4 = observable.observers_) == null || _observable$observers4.forEach(d => {
2056
2112
  if (d.dependenciesState_ === 1 /* IDerivationState_.POSSIBLY_STALE_ */) {
2057
2113
  d.dependenciesState_ = 2 /* IDerivationState_.STALE_ */;
2058
2114
  } else if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */ // this happens during computing of `d`, just keep lowestObserverState up to date.
@@ -2064,12 +2120,13 @@ function propagateChangeConfirmed(observable) {
2064
2120
  }
2065
2121
  // Used by computed when its dependency changed, but we don't wan't to immediately recompute.
2066
2122
  function propagateMaybeChanged(observable) {
2123
+ var _observable$observers5;
2067
2124
  // invariantLOS(observable, "maybe start");
2068
2125
  if (observable.lowestObserverState_ !== 0 /* IDerivationState_.UP_TO_DATE_ */) {
2069
2126
  return;
2070
2127
  }
2071
2128
  observable.lowestObserverState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
2072
- observable.observers_.forEach(d => {
2129
+ (_observable$observers5 = observable.observers_) == null || _observable$observers5.forEach(d => {
2073
2130
  if (d.dependenciesState_ === 0 /* IDerivationState_.UP_TO_DATE_ */) {
2074
2131
  d.dependenciesState_ = 1 /* IDerivationState_.POSSIBLY_STALE_ */;
2075
2132
  d.onBecomeStale_();
@@ -4336,31 +4393,16 @@ class ObservableSet {
4336
4393
  });
4337
4394
  }
4338
4395
  intersection(otherSet) {
4339
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4340
- return otherSet.intersection(this);
4341
- } else {
4342
- const dehancedSet = new Set(this);
4343
- return dehancedSet.intersection(otherSet);
4344
- }
4396
+ return new Set(this).intersection(otherSet);
4345
4397
  }
4346
4398
  union(otherSet) {
4347
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4348
- return otherSet.union(this);
4349
- } else {
4350
- const dehancedSet = new Set(this);
4351
- return dehancedSet.union(otherSet);
4352
- }
4399
+ return new Set(this).union(otherSet);
4353
4400
  }
4354
4401
  difference(otherSet) {
4355
4402
  return new Set(this).difference(otherSet);
4356
4403
  }
4357
4404
  symmetricDifference(otherSet) {
4358
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4359
- return otherSet.symmetricDifference(this);
4360
- } else {
4361
- const dehancedSet = new Set(this);
4362
- return dehancedSet.symmetricDifference(otherSet);
4363
- }
4405
+ return new Set(this).symmetricDifference(otherSet);
4364
4406
  }
4365
4407
  isSubsetOf(otherSet) {
4366
4408
  return new Set(this).isSubsetOf(otherSet);
@@ -4369,12 +4411,7 @@ class ObservableSet {
4369
4411
  return new Set(this).isSupersetOf(otherSet);
4370
4412
  }
4371
4413
  isDisjointFrom(otherSet) {
4372
- if (isES6Set(otherSet) && !isObservableSet(otherSet)) {
4373
- return otherSet.isDisjointFrom(this);
4374
- } else {
4375
- const dehancedSet = new Set(this);
4376
- return dehancedSet.isDisjointFrom(otherSet);
4377
- }
4414
+ return new Set(this).isDisjointFrom(otherSet);
4378
4415
  }
4379
4416
  replace(other) {
4380
4417
  if (isObservableSet(other)) {