mobx 6.2.0 → 6.3.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mobx",
3
- "version": "6.2.0",
3
+ "version": "6.3.3",
4
4
  "description": "Simple, scalable state management.",
5
5
  "source": "src/mobx.ts",
6
6
  "main": "dist/index.js",
@@ -86,14 +86,14 @@ export function autorun(
86
86
  return reaction.getDisposer_()
87
87
  }
88
88
 
89
- export type IReactionOptions = IAutorunOptions & {
89
+ export type IReactionOptions<T> = IAutorunOptions & {
90
90
  fireImmediately?: boolean
91
- equals?: IEqualsComparer<any>
91
+ equals?: IEqualsComparer<T>
92
92
  }
93
93
 
94
94
  const run = (f: Lambda) => f()
95
95
 
96
- function createSchedulerFromOptions(opts: IReactionOptions) {
96
+ function createSchedulerFromOptions(opts: IAutorunOptions) {
97
97
  return opts.scheduler
98
98
  ? opts.scheduler
99
99
  : opts.delay
@@ -104,7 +104,7 @@ function createSchedulerFromOptions(opts: IReactionOptions) {
104
104
  export function reaction<T>(
105
105
  expression: (r: IReactionPublic) => T,
106
106
  effect: (arg: T, prev: T, r: IReactionPublic) => void,
107
- opts: IReactionOptions = EMPTY_OBJECT
107
+ opts: IReactionOptions<T> = EMPTY_OBJECT
108
108
  ): IReactionDisposer {
109
109
  if (__DEV__) {
110
110
  if (!isFunction(expression) || !isFunction(effect))
@@ -124,7 +124,7 @@ export function reaction<T>(
124
124
  let value: T
125
125
  let oldValue: T = undefined as any // only an issue with fireImmediately
126
126
 
127
- const equals = (opts as any).compareStructural
127
+ const equals: IEqualsComparer<T> = (opts as any).compareStructural
128
128
  ? comparer.structural
129
129
  : opts.equals || comparer.default
130
130
 
package/src/api/flow.ts CHANGED
@@ -6,7 +6,8 @@ import {
6
6
  Annotation,
7
7
  isStringish,
8
8
  storeAnnotation,
9
- createFlowAnnotation
9
+ createFlowAnnotation,
10
+ createDecoratorAnnotation
10
11
  } from "../internal"
11
12
 
12
13
  export const FLOW = "flow"
@@ -28,9 +29,11 @@ interface Flow extends Annotation, PropertyDecorator {
28
29
  <R, Args extends any[]>(
29
30
  generator: (...args: Args) => Generator<any, R, any> | AsyncGenerator<any, R, any>
30
31
  ): (...args: Args) => CancellablePromise<R>
32
+ bound: Annotation & PropertyDecorator
31
33
  }
32
34
 
33
35
  const flowAnnotation = createFlowAnnotation("flow")
36
+ const flowBoundAnnotation = createFlowAnnotation("flow.bound", { bound: true })
34
37
 
35
38
  export const flow: Flow = Object.assign(
36
39
  function flow(arg1, arg2?) {
@@ -123,6 +126,8 @@ export const flow: Flow = Object.assign(
123
126
  flowAnnotation
124
127
  )
125
128
 
129
+ flow.bound = createDecoratorAnnotation(flowBoundAnnotation)
130
+
126
131
  function cancelPromise(promise) {
127
132
  if (isFunction(promise.cancel)) promise.cancel()
128
133
  }
@@ -158,3 +158,17 @@ export function get(obj: any, key: any): any {
158
158
  }
159
159
  die(11)
160
160
  }
161
+
162
+ export function apiDefineProperty(obj: Object, key: PropertyKey, descriptor: PropertyDescriptor) {
163
+ if (isObservableObject(obj)) {
164
+ return ((obj as any) as IIsObservableObject)[$mobx].defineProperty_(key, descriptor)
165
+ }
166
+ die(39)
167
+ }
168
+
169
+ export function apiOwnKeys(obj: Object) {
170
+ if (isObservableObject(obj)) {
171
+ return ((obj as any) as IIsObservableObject)[$mobx].ownKeys_()
172
+ }
173
+ die(38)
174
+ }
@@ -60,14 +60,14 @@ export function asCreateObservableOptions(thing: any): CreateObservableOptions {
60
60
  return thing || defaultCreateObservableOptions
61
61
  }
62
62
 
63
- const observableAnnotation = createObservableAnnotation("observable")
64
- const observableRefAnnotation = createObservableAnnotation("observable.ref", {
63
+ const observableAnnotation = createObservableAnnotation(OBSERVABLE)
64
+ const observableRefAnnotation = createObservableAnnotation(OBSERVABLE_REF, {
65
65
  enhancer: referenceEnhancer
66
66
  })
67
- const observableShallowAnnotation = createObservableAnnotation("observable.shallow", {
67
+ const observableShallowAnnotation = createObservableAnnotation(OBSERVABLE_SHALLOW, {
68
68
  enhancer: shallowEnhancer
69
69
  })
70
- const observableStructAnnotation = createObservableAnnotation("observable.struct", {
70
+ const observableStructAnnotation = createObservableAnnotation(OBSERVABLE_STRUCT, {
71
71
  enhancer: refStructEnhancer
72
72
  })
73
73
  const observableDecoratorAnnotation = createDecoratorAnnotation(observableAnnotation)
package/src/api/tojs.ts CHANGED
@@ -4,9 +4,9 @@ import {
4
4
  isObservableValue,
5
5
  isObservableMap,
6
6
  isObservableSet,
7
+ isComputedValue,
7
8
  die,
8
- IIsObservableObject,
9
- $mobx,
9
+ apiOwnKeys,
10
10
  objectPrototype
11
11
  } from "../internal"
12
12
 
@@ -24,7 +24,8 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
24
24
  )
25
25
  return source
26
26
 
27
- if (isObservableValue(source)) return toJSHelper(source.get(), __alreadySeen)
27
+ if (isObservableValue(source) || isComputedValue(source))
28
+ return toJSHelper(source.get(), __alreadySeen)
28
29
  if (__alreadySeen.has(source)) {
29
30
  return __alreadySeen.get(source)
30
31
  }
@@ -51,7 +52,7 @@ function toJSHelper(source, __alreadySeen: Map<any, any>) {
51
52
  } else {
52
53
  // must be observable object
53
54
  const res = cache(__alreadySeen, source, {})
54
- ;((source as any) as IIsObservableObject)[$mobx].ownKeys_().forEach((key: any) => {
55
+ apiOwnKeys(source).forEach((key: any) => {
55
56
  if (objectPrototype.propertyIsEnumerable.call(source, key)) {
56
57
  res[key] = toJSHelper(source[key], __alreadySeen)
57
58
  }
package/src/api/when.ts CHANGED
@@ -33,10 +33,10 @@ export function when(predicate: any, arg1?: any, arg2?: any): any {
33
33
  function _when(predicate: () => boolean, effect: Lambda, opts: IWhenOptions): IReactionDisposer {
34
34
  let timeoutHandle: any
35
35
  if (typeof opts.timeout === "number") {
36
+ const error = new Error("WHEN_TIMEOUT")
36
37
  timeoutHandle = setTimeout(() => {
37
38
  if (!disposer[$mobx].isDisposed_) {
38
39
  disposer()
39
- const error = new Error("WHEN_TIMEOUT")
40
40
  if (opts.onError) opts.onError(error)
41
41
  else throw error
42
42
  }
@@ -255,6 +255,11 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
255
255
  if (!this.keepAlive_) {
256
256
  clearObserving(this)
257
257
  this.value_ = undefined // don't hold on to computed value!
258
+ if (__DEV__ && this.isTracing_ !== TraceMode.NONE) {
259
+ console.log(
260
+ `[mobx.trace] Computed value '${this.name_}' was suspended and it will recompute on the next access.`
261
+ )
262
+ }
258
263
  }
259
264
  }
260
265
 
@@ -283,17 +288,14 @@ export class ComputedValue<T> implements IObservable, IComputedValue<T>, IDeriva
283
288
 
284
289
  warnAboutUntrackedRead_() {
285
290
  if (!__DEV__) return
286
- if (this.requiresReaction_ === true) {
287
- die(`[mobx] Computed value ${this.name_} is read outside a reactive context`)
288
- }
289
291
  if (this.isTracing_ !== TraceMode.NONE) {
290
292
  console.log(
291
- `[mobx.trace] '${this.name_}' is being read outside a reactive context. Doing a full recompute`
293
+ `[mobx.trace] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.`
292
294
  )
293
295
  }
294
- if (globalState.computedRequiresReaction) {
296
+ if (globalState.computedRequiresReaction || this.requiresReaction_) {
295
297
  console.warn(
296
- `[mobx] Computed value ${this.name_} is being read outside a reactive context. Doing a full recompute`
298
+ `[mobx] Computed value '${this.name_}' is being read outside a reactive context. Doing a full recompute.`
297
299
  )
298
300
  }
299
301
  }
@@ -149,7 +149,9 @@ export function checkIfStateModificationsAreAllowed(atom: IAtom) {
149
149
 
150
150
  export function checkIfStateReadsAreAllowed(observable: IObservable) {
151
151
  if (__DEV__ && !globalState.allowStateReads && globalState.observableRequiresReaction) {
152
- console.warn(`[mobx] Observable ${observable.name_} being read outside a reactive context`)
152
+ console.warn(
153
+ `[mobx] Observable '${observable.name_}' being read outside a reactive context.`
154
+ )
153
155
  }
154
156
  }
155
157
 
@@ -195,7 +197,7 @@ function warnAboutDerivationWithoutDependencies(derivation: IDerivation) {
195
197
 
196
198
  if (globalState.reactionRequiresObservable || derivation.requiresObservable_) {
197
199
  console.warn(
198
- `[mobx] Derivation ${derivation.name_} is created/updated without reading any observable value`
200
+ `[mobx] Derivation '${derivation.name_}' is created/updated without reading any observable value.`
199
201
  )
200
202
  }
201
203
  }
package/src/errors.ts CHANGED
@@ -3,6 +3,17 @@ const niceErrors = {
3
3
  1(annotationType, key: PropertyKey) {
4
4
  return `Cannot apply '${annotationType}' to '${key.toString()}': Field not found.`
5
5
  },
6
+ /*
7
+ 2(prop) {
8
+ return `invalid decorator for '${prop.toString()}'`
9
+ },
10
+ 3(prop) {
11
+ return `Cannot decorate '${prop.toString()}': action can only be used on properties with a function value.`
12
+ },
13
+ 4(prop) {
14
+ return `Cannot decorate '${prop.toString()}': computed can only be used on getter properties.`
15
+ },
16
+ */
6
17
  5: "'keys()' can only be used on observable objects, arrays, sets and maps",
7
18
  6: "'values()' can only be used on observable objects, arrays, sets and maps",
8
19
  7: "'entries()' can only be used on observable objects, arrays and maps",
@@ -59,7 +70,9 @@ const niceErrors = {
59
70
  36: "isolateGlobalState should be called before MobX is running any reactions",
60
71
  37(method) {
61
72
  return `[mobx] \`observableArray.${method}()\` mutates the array in-place, which is not allowed inside a derivation. Use \`array.slice().${method}()\` instead`
62
- }
73
+ },
74
+ 38: "'ownKeys()' can only be used on observable objects",
75
+ 39: "'defineProperty()' can only be used on observable objects"
63
76
  } as const
64
77
 
65
78
  const errors: typeof niceErrors = __DEV__ ? niceErrors : ({} as any)
package/src/mobx.ts CHANGED
@@ -17,7 +17,7 @@
17
17
  */
18
18
  import { die } from "./errors"
19
19
  import { getGlobal } from "./utils/global"
20
- ;["Symbol", "Map", "Set", "Symbol"].forEach(m => {
20
+ ;["Symbol", "Map", "Set"].forEach(m => {
21
21
  let g = getGlobal()
22
22
  if (typeof g[m] === "undefined") {
23
23
  die(`MobX requires global '${m}' to be available or polyfilled`)
@@ -100,6 +100,8 @@ export {
100
100
  remove,
101
101
  has,
102
102
  get,
103
+ apiOwnKeys as ownKeys,
104
+ apiDefineProperty as defineProperty,
103
105
  configure,
104
106
  onBecomeObserved,
105
107
  onBecomeUnobserved,
@@ -58,7 +58,8 @@ function make_(
58
58
  // function on proto -> autoAction/flow
59
59
  if (source !== adm.target_ && typeof descriptor.value === "function") {
60
60
  if (isGenerator(descriptor.value)) {
61
- return flow.make_(adm, key, descriptor, source)
61
+ const flowAnnotation = this.options_?.autoBind ? flow.bound : flow
62
+ return flowAnnotation.make_(adm, key, descriptor, source)
62
63
  }
63
64
  const actionAnnotation = this.options_?.autoBind ? autoAction.bound : autoAction
64
65
  return actionAnnotation.make_(adm, key, descriptor, source)
@@ -64,7 +64,7 @@ const objectProxyTraps: ProxyHandler<any> = {
64
64
  ownKeys(target: IIsObservableObject): PropertyKey[] {
65
65
  if (__DEV__ && globalState.trackingDerivation)
66
66
  warnAboutProxyRequirement(
67
- "iterate keys to detect added / removed properties. Use `keys` from 'mobx' instead."
67
+ "iterate keys to detect added / removed properties. Use 'keys' from 'mobx' instead."
68
68
  )
69
69
  return getAdm(target).ownKeys_()
70
70
  },
@@ -32,12 +32,16 @@ function make_(
32
32
  : MakeResult.Continue
33
33
  }
34
34
  // prototype
35
+ // bound - must annotate protos to support super.flow()
36
+ if (this.options_?.bound && !isFlow(adm.target_[key])) {
37
+ if (this.extend_(adm, key, descriptor, false) === null) return MakeResult.Cancel
38
+ }
35
39
  if (isFlow(descriptor.value)) {
36
40
  // A prototype could have been annotated already by other constructor,
37
41
  // rest of the proto chain must be annotated already
38
42
  return MakeResult.Break
39
43
  }
40
- const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, false)
44
+ const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, false, false)
41
45
  defineProperty(source, key, flowDescriptor)
42
46
  return MakeResult.Continue
43
47
  }
@@ -48,7 +52,7 @@ function extend_(
48
52
  descriptor: PropertyDescriptor,
49
53
  proxyTrap: boolean
50
54
  ): boolean | null {
51
- const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor)
55
+ const flowDescriptor = createFlowDescriptor(adm, this, key, descriptor, this.options_?.bound)
52
56
  return adm.defineProperty_(key, flowDescriptor, proxyTrap)
53
57
  }
54
58
 
@@ -71,12 +75,17 @@ function createFlowDescriptor(
71
75
  annotation: Annotation,
72
76
  key: PropertyKey,
73
77
  descriptor: PropertyDescriptor,
78
+ bound: boolean,
74
79
  // provides ability to disable safeDescriptors for prototypes
75
80
  safeDescriptors: boolean = globalState.safeDescriptors
76
81
  ): PropertyDescriptor {
77
82
  assertFlowDescriptor(adm, annotation, key, descriptor)
83
+ let { value } = descriptor
84
+ if (bound) {
85
+ value = value.bind(adm.proxy_ ?? adm.target_)
86
+ }
78
87
  return {
79
- value: flow(descriptor.value),
88
+ value: flow(value),
80
89
  // Non-configurable for classes
81
90
  // prevents accidental field redefinition in subclass
82
91
  configurable: safeDescriptors ? adm.isPlainObject_ : true,
@@ -17,7 +17,11 @@ function shallowComparer(a: any, b: any): boolean {
17
17
  }
18
18
 
19
19
  function defaultComparer(a: any, b: any): boolean {
20
- return Object.is(a, b)
20
+ if (Object.is) return Object.is(a, b)
21
+
22
+ return a === b
23
+ ? a !== 0 || 1 / a === 1 / b
24
+ : a !== a && b !== b
21
25
  }
22
26
 
23
27
  export const comparer = {