mobx 7.0.4 → 7.0.6
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 +18 -0
- package/dist/core/reaction.d.ts +1 -0
- package/dist/mobx.cjs.development.js +74 -41
- package/dist/mobx.cjs.development.js.map +1 -1
- package/dist/mobx.cjs.production.min.js +1 -1
- package/dist/mobx.cjs.production.min.js.map +1 -1
- package/dist/mobx.esm.development.js +74 -41
- package/dist/mobx.esm.development.js.map +1 -1
- package/dist/mobx.esm.js +74 -41
- package/dist/mobx.esm.js.map +1 -1
- package/dist/mobx.esm.production.min.js +1 -1
- package/dist/mobx.esm.production.min.js.map +1 -1
- package/dist/mobx.mjs +74 -41
- package/dist/mobx.mjs.map +1 -1
- package/dist/mobx.umd.development.js +74 -41
- package/dist/mobx.umd.development.js.map +1 -1
- package/dist/mobx.umd.production.min.js +1 -1
- package/dist/mobx.umd.production.min.js.map +1 -1
- package/dist/types/autoannotation.d.ts +2 -1
- package/package.json +1 -1
- package/src/api/observable.ts +2 -2
- package/src/core/observable.ts +33 -25
- package/src/core/reaction.ts +8 -5
- package/src/types/autoannotation.ts +13 -1
- package/src/types/intercept-utils.ts +7 -2
- package/src/types/listen-utils.ts +7 -2
- package/src/types/observablearray.ts +2 -3
- package/src/types/observableset.ts +2 -1
- package/src/types/observablevalue.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# mobx
|
|
2
2
|
|
|
3
|
+
## 7.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`4b1c8d3ea2b3de1a2ae852fd5631e33d28a82b15`](https://github.com/mobxjs/mobx/commit/4b1c8d3ea2b3de1a2ae852fd5631e33d28a82b15) [#4721](https://github.com/mobxjs/mobx/pull/4721) Thanks [@giaBaoJS](https://github.com/giaBaoJS)! - fix: assigning `length` on an observable array now respects `intercept`. A cancelled or modified splice used to be overridden by a direct write to the backing array, which truncated it without notifying observers and made the next mutation throw "Modification exception". A non-integer `length` now throws "Out of range" before touching the array.
|
|
8
|
+
|
|
9
|
+
- [`60be47cab0926d12f22449ec6c809fb5ec6601c4`](https://github.com/mobxjs/mobx/commit/60be47cab0926d12f22449ec6c809fb5ec6601c4) [#4716](https://github.com/mobxjs/mobx/pull/4716) Thanks [@AcEKaycgR](https://github.com/AcEKaycgR)! - fix: `observable.box(array)` no longer warns under `observableRequiresReaction` when a spy is active. The `create` spy event now reports `newValue` as the value itself rather than a string, matching the declared `IBoxDidChange` type. Previously the constructor stringified the value, and stringifying an observable array reads its `length`, which counted as an observable read outside a reactive context. Spy listeners that relied on `newValue` being a string for `create` events will now receive the raw value.
|
|
10
|
+
|
|
11
|
+
- [`3551aeb051ff922717b03c4fbc04a6b98f7f3b3e`](https://github.com/mobxjs/mobx/commit/3551aeb051ff922717b03c4fbc04a6b98f7f3b3e) [#4717](https://github.com/mobxjs/mobx/pull/4717) Thanks [@breken-ai](https://github.com/breken-ai)! - Remove the `signal` abort listener when a reaction disposes itself (`r.dispose()` or a resolved `when`), so long-lived `AbortSignal`s no longer accumulate listeners that keep disposed reactions in memory
|
|
12
|
+
|
|
13
|
+
- [`7e4e27dbfbf0b56770a11e9cba913e5d49c0c6f8`](https://github.com/mobxjs/mobx/commit/7e4e27dbfbf0b56770a11e9cba913e5d49c0c6f8) [#4718](https://github.com/mobxjs/mobx/pull/4718) Thanks [@breken-ai](https://github.com/breken-ai)! - `observe`/`spy` `add` events of an observable Set now report the value that was actually stored (e.g. the observable copy of a plain object), matching ObservableMap and observable arrays, so `set.has(change.newValue)` holds
|
|
14
|
+
|
|
15
|
+
## 7.0.5
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- [`105d66d73529812a8eddc0847891e7f0c574e129`](https://github.com/mobxjs/mobx/commit/105d66d73529812a8eddc0847891e7f0c574e129) [#4713](https://github.com/mobxjs/mobx/pull/4713) Thanks [@xaviergonz](https://github.com/xaviergonz)! - perf: reduce memory used by large, mostly unobserved state. `intercept` / `observe` handler arrays are allocated at their exact size, an observable's internal observers `Set` is released once its last observer leaves, observable objects created with the same `deep` / `autoBind` options share one default annotation instead of allocating their own, and `endBatch()` skips the unobservation pass when nothing is pending.
|
|
20
|
+
|
|
3
21
|
## 7.0.4
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/dist/core/reaction.d.ts
CHANGED
|
@@ -35,6 +35,7 @@ export declare class Reaction implements IDerivation, IReactionPublic {
|
|
|
35
35
|
runId_: number;
|
|
36
36
|
unboundDepsCount_: number;
|
|
37
37
|
private flags_;
|
|
38
|
+
private removeAbortListener_;
|
|
38
39
|
constructor(name_: string | undefined, onInvalidate_: () => void, errorHandler_?: ((error: any, derivation: IDerivation) => void) | undefined, requiresObservable_?: any);
|
|
39
40
|
get isDisposed(): boolean;
|
|
40
41
|
set isDisposed(newValue: boolean);
|
|
@@ -820,6 +820,19 @@ function createAutoAnnotation(options) {
|
|
|
820
820
|
extend_
|
|
821
821
|
};
|
|
822
822
|
}
|
|
823
|
+
// The auto annotation only depends on `deep` and `autoBind`, so share one instance per combination
|
|
824
|
+
// instead of allocating one for every object created with options
|
|
825
|
+
const autoAnnotations = [autoAnnotation];
|
|
826
|
+
function getAutoAnnotation(options) {
|
|
827
|
+
var _autoAnnotations$idx;
|
|
828
|
+
const deep = options.deep !== false;
|
|
829
|
+
const autoBind = !!options.autoBind;
|
|
830
|
+
const idx = (deep ? 0 : 1) | (autoBind ? 2 : 0);
|
|
831
|
+
return (_autoAnnotations$idx = autoAnnotations[idx]) != null ? _autoAnnotations$idx : autoAnnotations[idx] = createAutoAnnotation({
|
|
832
|
+
deep,
|
|
833
|
+
autoBind
|
|
834
|
+
});
|
|
835
|
+
}
|
|
823
836
|
function make_$1(adm, key, descriptor, source) {
|
|
824
837
|
var _this$options_3, _this$options_4;
|
|
825
838
|
// getter -> computed
|
|
@@ -936,7 +949,7 @@ function getEnhancerFromOptions(options) {
|
|
|
936
949
|
}
|
|
937
950
|
function getAnnotationFromOptions(options) {
|
|
938
951
|
var _options$defaultDecor;
|
|
939
|
-
return options ? (_options$defaultDecor = options.defaultDecorator) != null ? _options$defaultDecor :
|
|
952
|
+
return options ? (_options$defaultDecor = options.defaultDecorator) != null ? _options$defaultDecor : getAutoAnnotation(options) : undefined;
|
|
940
953
|
}
|
|
941
954
|
function getEnhancerFromAnnotation(annotation) {
|
|
942
955
|
var _annotation$options_$, _annotation$options_;
|
|
@@ -1182,14 +1195,13 @@ class ObservableValue extends Atom {
|
|
|
1182
1195
|
this.equals_ = equals_;
|
|
1183
1196
|
this.value_ = enhancer_(value, undefined, name_);
|
|
1184
1197
|
if (notifySpy && isSpyEnabled()) {
|
|
1185
|
-
var _this$value_;
|
|
1186
1198
|
// only notify spy if this is a stand-alone observable
|
|
1187
1199
|
spyReport({
|
|
1188
1200
|
type: CREATE,
|
|
1189
1201
|
object: this,
|
|
1190
1202
|
observableKind: "value",
|
|
1191
1203
|
debugObjectName: this.name_,
|
|
1192
|
-
newValue:
|
|
1204
|
+
newValue: this.value_
|
|
1193
1205
|
});
|
|
1194
1206
|
}
|
|
1195
1207
|
}
|
|
@@ -1988,34 +2000,41 @@ function endBatch() {
|
|
|
1988
2000
|
// the nested call instead of recursing; the outer loop re-reads list.length on
|
|
1989
2001
|
// every iteration, so it picks up anything the nested dispose() pushes onto the
|
|
1990
2002
|
// same pendingUnobservations array.
|
|
1991
|
-
if (!globalState.isRunningUnobservations) {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2003
|
+
if (!globalState.isRunningUnobservations && globalState.pendingUnobservations.length > 0) {
|
|
2004
|
+
runPendingUnobservations();
|
|
2005
|
+
}
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
// Only called when there is something to unobserve, so the common endBatch() skips the
|
|
2009
|
+
// try/finally and the pendingUnobservations reallocation entirely
|
|
2010
|
+
function runPendingUnobservations() {
|
|
2011
|
+
globalState.isRunningUnobservations = true;
|
|
2012
|
+
try {
|
|
2013
|
+
const list = globalState.pendingUnobservations;
|
|
2014
|
+
for (let i = 0; i < list.length; i++) {
|
|
2015
|
+
const observable = list[i];
|
|
2016
|
+
observable.isPendingUnobservation = false;
|
|
2017
|
+
if (!observable.observers_ || observable.observers_.size === 0) {
|
|
2018
|
+
// release the empty Set so unobserved atoms don't keep paying for it
|
|
2019
|
+
observable.observers_ = null;
|
|
2020
|
+
if (observable.isBeingObserved) {
|
|
2021
|
+
// if this observable had reactive observers, trigger the hooks
|
|
2022
|
+
observable.isBeingObserved = false;
|
|
2023
|
+
observable.onBUO();
|
|
2024
|
+
}
|
|
2025
|
+
if (observable instanceof ComputedValue) {
|
|
2026
|
+
// computed values are automatically teared down when the last observer leaves
|
|
2027
|
+
// this process happens recursively, this computed might be the last observabe of another, etc..
|
|
2028
|
+
observable.suspend_();
|
|
2010
2029
|
}
|
|
2011
|
-
globalState.pendingUnobservations = [];
|
|
2012
|
-
} finally {
|
|
2013
|
-
// Always release the guard, even if an onBUO handler (user code) threw,
|
|
2014
|
-
// otherwise every future endBatch() would see isRunningUnobservations
|
|
2015
|
-
// stuck true and silently stop draining pendingUnobservations forever.
|
|
2016
|
-
globalState.isRunningUnobservations = false;
|
|
2017
2030
|
}
|
|
2018
2031
|
}
|
|
2032
|
+
globalState.pendingUnobservations = [];
|
|
2033
|
+
} finally {
|
|
2034
|
+
// Always release the guard, even if an onBUO handler (user code) threw,
|
|
2035
|
+
// otherwise every future endBatch() would see isRunningUnobservations
|
|
2036
|
+
// stuck true and silently stop draining pendingUnobservations forever.
|
|
2037
|
+
globalState.isRunningUnobservations = false;
|
|
2019
2038
|
}
|
|
2020
2039
|
}
|
|
2021
2040
|
/**
|
|
@@ -2145,6 +2164,7 @@ class Reaction {
|
|
|
2145
2164
|
this.runId_ = 0;
|
|
2146
2165
|
this.unboundDepsCount_ = 0;
|
|
2147
2166
|
this.flags_ = 0b00000;
|
|
2167
|
+
this.removeAbortListener_ = undefined;
|
|
2148
2168
|
this.name_ = name_;
|
|
2149
2169
|
this.onInvalidate_ = onInvalidate_;
|
|
2150
2170
|
this.errorHandler_ = errorHandler_;
|
|
@@ -2281,7 +2301,10 @@ class Reaction {
|
|
|
2281
2301
|
}
|
|
2282
2302
|
dispose() {
|
|
2283
2303
|
if (!this.isDisposed) {
|
|
2304
|
+
var _this$removeAbortList;
|
|
2284
2305
|
this.isDisposed = true;
|
|
2306
|
+
// also covers self-disposal (`r.dispose()`, `when`), which bypasses the disposer
|
|
2307
|
+
(_this$removeAbortList = this.removeAbortListener_) == null || _this$removeAbortList.call(this);
|
|
2285
2308
|
if (!this.isRunning) {
|
|
2286
2309
|
// if disposed while running, clean up later. Maybe not optimal, but rare case
|
|
2287
2310
|
startBatch();
|
|
@@ -2291,11 +2314,11 @@ class Reaction {
|
|
|
2291
2314
|
}
|
|
2292
2315
|
}
|
|
2293
2316
|
getDisposer_(abortSignal) {
|
|
2294
|
-
const dispose = () =>
|
|
2295
|
-
|
|
2296
|
-
abortSignal
|
|
2297
|
-
|
|
2298
|
-
|
|
2317
|
+
const dispose = () => this.dispose();
|
|
2318
|
+
if (abortSignal != null && abortSignal.addEventListener && !this.isDisposed) {
|
|
2319
|
+
abortSignal.addEventListener("abort", dispose);
|
|
2320
|
+
this.removeAbortListener_ = () => abortSignal.removeEventListener == null ? void 0 : abortSignal.removeEventListener("abort", dispose);
|
|
2321
|
+
}
|
|
2299
2322
|
dispose[$mobx] = this;
|
|
2300
2323
|
if ("dispose" in Symbol && typeof Symbol.dispose === "symbol") {
|
|
2301
2324
|
dispose[Symbol.dispose] = dispose;
|
|
@@ -3294,8 +3317,13 @@ function hasInterceptors(interceptable) {
|
|
|
3294
3317
|
return interceptable.interceptors_ !== undefined && interceptable.interceptors_.length > 0;
|
|
3295
3318
|
}
|
|
3296
3319
|
function registerInterceptor(interceptable, handler) {
|
|
3297
|
-
|
|
3298
|
-
interceptors
|
|
3320
|
+
let interceptors = interceptable.interceptors_;
|
|
3321
|
+
if (interceptors) {
|
|
3322
|
+
interceptors.push(handler);
|
|
3323
|
+
} else {
|
|
3324
|
+
// Allocate at the exact size: pushing onto an empty array over-allocates its backing store
|
|
3325
|
+
interceptors = interceptable.interceptors_ = [handler];
|
|
3326
|
+
}
|
|
3299
3327
|
return once(() => {
|
|
3300
3328
|
const idx = interceptors.indexOf(handler);
|
|
3301
3329
|
if (idx !== -1) {
|
|
@@ -3327,8 +3355,13 @@ function hasListeners(listenable) {
|
|
|
3327
3355
|
return listenable.changeListeners_ !== undefined && listenable.changeListeners_.length > 0;
|
|
3328
3356
|
}
|
|
3329
3357
|
function registerListener(listenable, handler) {
|
|
3330
|
-
|
|
3331
|
-
listeners
|
|
3358
|
+
let listeners = listenable.changeListeners_;
|
|
3359
|
+
if (listeners) {
|
|
3360
|
+
listeners.push(handler);
|
|
3361
|
+
} else {
|
|
3362
|
+
// Allocate at the exact size: pushing onto an empty array over-allocates its backing store
|
|
3363
|
+
listeners = listenable.changeListeners_ = [handler];
|
|
3364
|
+
}
|
|
3332
3365
|
return once(() => {
|
|
3333
3366
|
const idx = listeners.indexOf(handler);
|
|
3334
3367
|
if (idx !== -1) {
|
|
@@ -3442,8 +3475,7 @@ const arrayTraps = {
|
|
|
3442
3475
|
const adm = target[$mobx];
|
|
3443
3476
|
if (name === "length") {
|
|
3444
3477
|
adm.setArrayLength_(value);
|
|
3445
|
-
}
|
|
3446
|
-
if (typeof name === "symbol" || isNaN(name)) {
|
|
3478
|
+
} else if (typeof name === "symbol" || isNaN(name)) {
|
|
3447
3479
|
target[name] = value;
|
|
3448
3480
|
} else {
|
|
3449
3481
|
// numeric string
|
|
@@ -3488,7 +3520,7 @@ class ObservableArrayAdministration {
|
|
|
3488
3520
|
return this.values_.length;
|
|
3489
3521
|
}
|
|
3490
3522
|
setArrayLength_(newLength) {
|
|
3491
|
-
if (
|
|
3523
|
+
if (!Number.isInteger(newLength) || newLength < 0) {
|
|
3492
3524
|
die(40, newLength);
|
|
3493
3525
|
}
|
|
3494
3526
|
let currentLength = this.values_.length;
|
|
@@ -4270,7 +4302,8 @@ class ObservableSet {
|
|
|
4270
4302
|
}
|
|
4271
4303
|
if (!this.has(value)) {
|
|
4272
4304
|
transaction(() => {
|
|
4273
|
-
this.
|
|
4305
|
+
value = this.enhancer_(value, undefined); // report the stored value, like map and array
|
|
4306
|
+
this.data_.add(value);
|
|
4274
4307
|
this.atom_.reportChanged();
|
|
4275
4308
|
});
|
|
4276
4309
|
const notifySpy = isSpyEnabled();
|