Skip to content

Commit 1751937

Browse files
committed
atom for array.length
- add an Atom for observing array.length - swap observation on `array.length` to the new `array.atomForLength_` - handle change reporting for splice events that change array length - when array.length is changed, create an action, `executeAction`, that reports the change of `array.atom_` and `array.atomForLength_`, to avoid double reactions - added getCurrentId() to abstract globalState.mobxGuid, needed to conserve id index on array length atom
1 parent 8b54ab1 commit 1751937

File tree

2 files changed

+64
-27
lines changed

2 files changed

+64
-27
lines changed

packages/mobx/src/types/observablearray.ts

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
checkIfStateModificationsAreAllowed,
1313
createInstanceofPredicate,
1414
getNextId,
15+
getCurrentId,
1516
hasInterceptors,
1617
hasListeners,
1718
interceptChange,
@@ -27,7 +28,8 @@ import {
2728
hasProp,
2829
die,
2930
globalState,
30-
initObservable
31+
initObservable,
32+
executeAction
3133
} from "../internal"
3234

3335
const SPLICE = "splice"
@@ -119,6 +121,7 @@ export class ObservableArrayAdministration
119121
implements IInterceptable<IArrayWillChange<any> | IArrayWillSplice<any>>, IListenable
120122
{
121123
atom_: IAtom
124+
atomForLength_: IAtom
122125
readonly values_: any[] = [] // this is the prop that gets proxied, so can't replace it!
123126
interceptors_
124127
changeListeners_
@@ -134,6 +137,12 @@ export class ObservableArrayAdministration
134137
public legacyMode_: boolean
135138
) {
136139
this.atom_ = new Atom(name)
140+
// id index is conserved across atom_ and atomForLength for id-index tracking unit tests
141+
let nameForLength = __DEV__
142+
? "ObservableArrayLength@" + getCurrentId()
143+
: "ObservableArrayLength"
144+
this.atomForLength_ = new Atom(nameForLength)
145+
137146
this.enhancer_ = (newV, oldV) =>
138147
enhancer(newV, oldV, __DEV__ ? name + "[..]" : "ObservableArray[..]")
139148
}
@@ -177,7 +186,7 @@ export class ObservableArrayAdministration
177186
}
178187

179188
getArrayLength_(): number {
180-
this.atom_.reportObserved()
189+
this.atomForLength_.reportObserved()
181190
return this.values_.length
182191
}
183192

@@ -313,33 +322,58 @@ export class ObservableArrayAdministration
313322
}
314323

315324
notifyArraySplice_(index: number, added: any[], removed: any[]) {
316-
const notifySpy = !this.owned_ && isSpyEnabled()
317-
const notify = hasListeners(this)
318-
const change: IArraySplice | null =
319-
notify || notifySpy
320-
? ({
321-
observableKind: "array",
322-
object: this.proxy_,
323-
debugObjectName: this.atom_.name_,
324-
type: SPLICE,
325-
index,
326-
removed,
327-
added,
328-
removedCount: removed.length,
329-
addedCount: added.length
330-
} as const)
331-
: null
325+
const lengthChanged = added.length - removed.length != 0
326+
327+
const handleChange = () => {
328+
const notifySpy = !this.owned_ && isSpyEnabled()
329+
const notify = hasListeners(this)
330+
// the change for x and x.length is the same except for the name, so use a function to create
331+
const makeNamedChange = (name): IArraySplice => {
332+
return {
333+
observableKind: "array",
334+
object: this.proxy_,
335+
debugObjectName: name,
336+
type: SPLICE,
337+
index,
338+
removed,
339+
added,
340+
removedCount: removed.length,
341+
addedCount: added.length
342+
} as const
343+
}
344+
const change: IArraySplice | null =
345+
notify || notifySpy ? makeNamedChange(this.atom_.name_) : null
332346

333-
if (__DEV__ && notifySpy) {
334-
spyReportStart(change!)
335-
}
336-
this.atom_.reportChanged()
337-
// conform: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe
338-
if (notify) {
339-
notifyListeners(this, change)
347+
if (__DEV__ && notifySpy) {
348+
spyReportStart(change!)
349+
}
350+
this.atom_.reportChanged()
351+
// conform: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/observe
352+
if (notify) {
353+
notifyListeners(this, change)
354+
}
355+
if (__DEV__ && notifySpy) {
356+
spyReportEnd()
357+
}
358+
359+
// Handle potential length change observable
360+
if (lengthChanged) {
361+
const changeLength: IArraySplice | null =
362+
notify || notifySpy ? makeNamedChange(this.atomForLength_.name_) : null
363+
if (__DEV__ && notifySpy) {
364+
spyReportStart(changeLength!)
365+
}
366+
this.atomForLength_.reportChanged()
367+
if (__DEV__ && notifySpy) {
368+
spyReportEnd()
369+
}
370+
}
340371
}
341-
if (__DEV__ && notifySpy) {
342-
spyReportEnd()
372+
if (lengthChanged) {
373+
// for cases where `x`, and `x.length` are observed, this function can create two events, so bottle them into an `action` to prevent multiple reactions
374+
executeAction("array-splice", true, handleChange)
375+
} else {
376+
handleChange()
343377
}
344378
}
345379

packages/mobx/src/utils/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ export function warnAboutProxyRequirement(msg: string) {
3939
}
4040
}
4141

42+
export function getCurrentId() {
43+
return globalState.mobxGuid
44+
}
4245
export function getNextId() {
4346
return ++globalState.mobxGuid
4447
}

0 commit comments

Comments
 (0)