@@ -12,6 +12,7 @@ import {
12
12
checkIfStateModificationsAreAllowed ,
13
13
createInstanceofPredicate ,
14
14
getNextId ,
15
+ getCurrentId ,
15
16
hasInterceptors ,
16
17
hasListeners ,
17
18
interceptChange ,
@@ -27,7 +28,8 @@ import {
27
28
hasProp ,
28
29
die ,
29
30
globalState ,
30
- initObservable
31
+ initObservable ,
32
+ executeAction
31
33
} from "../internal"
32
34
33
35
const SPLICE = "splice"
@@ -119,6 +121,7 @@ export class ObservableArrayAdministration
119
121
implements IInterceptable < IArrayWillChange < any > | IArrayWillSplice < any > > , IListenable
120
122
{
121
123
atom_ : IAtom
124
+ atomForLength_ : IAtom
122
125
readonly values_ : any [ ] = [ ] // this is the prop that gets proxied, so can't replace it!
123
126
interceptors_
124
127
changeListeners_
@@ -134,6 +137,12 @@ export class ObservableArrayAdministration
134
137
public legacyMode_ : boolean
135
138
) {
136
139
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
+
137
146
this . enhancer_ = ( newV , oldV ) =>
138
147
enhancer ( newV , oldV , __DEV__ ? name + "[..]" : "ObservableArray[..]" )
139
148
}
@@ -177,7 +186,7 @@ export class ObservableArrayAdministration
177
186
}
178
187
179
188
getArrayLength_ ( ) : number {
180
- this . atom_ . reportObserved ( )
189
+ this . atomForLength_ . reportObserved ( )
181
190
return this . values_ . length
182
191
}
183
192
@@ -313,33 +322,58 @@ export class ObservableArrayAdministration
313
322
}
314
323
315
324
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
332
346
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
+ }
340
371
}
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 ( )
343
377
}
344
378
}
345
379
0 commit comments