@@ -11,6 +11,7 @@ export type SubscriberFunction<T> = (value: T) => void;
11
11
export interface SubscriberObject < T > {
12
12
next : SubscriberFunction < T > ;
13
13
invalidate : ( ) => void ;
14
+ invalidated : boolean ;
14
15
}
15
16
16
17
/**
@@ -100,8 +101,12 @@ const bind = <T>(object: T | null | undefined, fnName: keyof T) => {
100
101
101
102
const toSubscriberObject = < T > ( subscriber : Subscriber < T > ) : SubscriberObject < T > =>
102
103
typeof subscriber === 'function'
103
- ? { next : subscriber . bind ( null ) , invalidate : noop }
104
- : { next : bind ( subscriber , 'next' ) , invalidate : bind ( subscriber , 'invalidate' ) } ;
104
+ ? { next : subscriber . bind ( null ) , invalidate : noop , invalidated : false }
105
+ : {
106
+ next : bind ( subscriber , 'next' ) ,
107
+ invalidate : bind ( subscriber , 'invalidate' ) ,
108
+ invalidated : false ,
109
+ } ;
105
110
106
111
const returnThis = function < T > ( this : T ) : T {
107
112
return this ;
@@ -164,6 +169,7 @@ export const batch = <T>(fn: () => T): T => {
164
169
if ( needsProcessQueue ) {
165
170
for ( const [ subscriberObject , value ] of queue ) {
166
171
queue . delete ( subscriberObject ) ;
172
+ subscriberObject . invalidated = false ;
167
173
subscriberObject . next ( value ) ;
168
174
}
169
175
willProcessQueue = false ;
@@ -223,6 +229,7 @@ export function get<T>(store: SubscribableStore<T>): T {
223
229
export abstract class Store < T > implements Readable < T > {
224
230
private _subscribers = new Set < SubscriberObject < T > > ( ) ;
225
231
private _cleanupFn : null | Unsubscriber = null ;
232
+ private _invalidated = false ;
226
233
227
234
/**
228
235
*
@@ -242,26 +249,43 @@ export abstract class Store<T> implements Readable<T> {
242
249
}
243
250
}
244
251
252
+ /**
253
+ * Notifies the store that it will receive a new value very soon.
254
+ * The notification will be propagated to all derived stores
255
+ * (if not done already).
256
+ * Note that after calling invalidate, it is required to
257
+ * call set to put the store back into a normal state.
258
+ */
259
+ protected invalidate ( ) : void {
260
+ if ( ! this . _invalidated ) {
261
+ this . _invalidated = true ;
262
+ for ( const subscriber of this . _subscribers ) {
263
+ if ( ! subscriber . invalidated ) {
264
+ subscriber . invalidated = true ;
265
+ subscriber . invalidate ( ) ;
266
+ }
267
+ }
268
+ }
269
+ }
270
+
245
271
/**
246
272
* Replaces store's state with the provided value.
247
273
* Equivalent of {@link Writable.set}, but internal to the store.
248
274
*
249
275
* @param value - value to be used as the new state of a store.
250
276
*/
251
277
protected set ( value : T ) : void {
252
- if ( notEqual ( this . _value , value ) ) {
278
+ if ( this . _invalidated || notEqual ( this . _value , value ) ) {
253
279
this . _value = value ;
254
280
if ( ! this . _cleanupFn ) {
255
281
// subscriber not yet initialized
256
282
return ;
257
283
}
258
284
batch ( ( ) => {
285
+ this . invalidate ( ) ;
286
+ this . _invalidated = false ;
259
287
for ( const subscriber of this . _subscribers ) {
260
- const needInvalidate = ! queue . has ( subscriber ) ;
261
288
queue . set ( subscriber , value ) ;
262
- if ( needInvalidate ) {
263
- subscriber . invalidate ( ) ;
264
- }
265
289
}
266
290
} ) ;
267
291
}
@@ -314,6 +338,10 @@ export abstract class Store<T> implements Readable<T> {
314
338
this . _start ( ) ;
315
339
}
316
340
subscriberObject . next ( this . _value ) ;
341
+ if ( this . _invalidated ) {
342
+ subscriberObject . invalidated = true ;
343
+ subscriberObject . invalidate ( ) ;
344
+ }
317
345
318
346
const unsubscribe = ( ) => {
319
347
const removed = this . _subscribers . delete ( subscriberObject ) ;
@@ -515,6 +543,7 @@ export abstract class DerivedStore<
515
543
} ,
516
544
invalidate : ( ) => {
517
545
pending |= 1 << idx ;
546
+ this . invalidate ( ) ;
518
547
} ,
519
548
} )
520
549
) ;
0 commit comments