66 * found in the LICENSE file at https://angular.io/license
77 */
88
9+ import {
10+ getActiveConsumer ,
11+ Consumer as InteropConsumer ,
12+ Signal as InteropSignal ,
13+ setActiveConsumer ,
14+ } from './interop_lib' ;
15+
916// Required as the signals library is in a separate package, so we need to explicitly ensure the
1017// global `ngDevMode` type is defined.
1118declare const ngDevMode : boolean | undefined ;
1219
13- /**
14- * The currently active consumer `ReactiveNode`, if running code in a reactive context.
15- *
16- * Change this via `setActiveConsumer`.
17- */
18- let activeConsumer : ReactiveNode | null = null ;
1920let inNotificationPhase = false ;
2021
21- type Version = number & { __brand : 'Version' } ;
22+ export type Version = number & { __brand : 'Version' } ;
2223
2324/**
2425 * Global epoch counter. Incremented whenever a source signal is set.
@@ -32,16 +33,6 @@ let epoch: Version = 1 as Version;
3233 */
3334export const SIGNAL = /* @__PURE__ */ Symbol ( 'SIGNAL' ) ;
3435
35- export function setActiveConsumer ( consumer : ReactiveNode | null ) : ReactiveNode | null {
36- const prev = activeConsumer ;
37- activeConsumer = consumer ;
38- return prev ;
39- }
40-
41- export function getActiveConsumer ( ) : ReactiveNode | null {
42- return activeConsumer ;
43- }
44-
4536export function isInNotificationPhase ( ) : boolean {
4637 return inNotificationPhase ;
4738}
@@ -53,7 +44,6 @@ export interface Reactive {
5344export function isReactive ( value : unknown ) : value is Reactive {
5445 return ( value as Partial < Reactive > ) [ SIGNAL ] !== undefined ;
5546}
56-
5747export const REACTIVE_NODE : ReactiveNode = {
5848 version : 0 as Version ,
5949 lastCleanEpoch : 0 as Version ,
@@ -62,6 +52,7 @@ export const REACTIVE_NODE: ReactiveNode = {
6252 producerLastReadVersion : undefined ,
6353 producerIndexOfThis : undefined ,
6454 nextProducerIndex : 0 ,
55+ hasInteropSignalDep : false ,
6556 liveConsumerNode : undefined ,
6657 liveConsumerIndexOfThis : undefined ,
6758 consumerAllowSignalWrites : false ,
@@ -143,6 +134,11 @@ export interface ReactiveNode {
143134 */
144135 nextProducerIndex : number ;
145136
137+ /**
138+ * Whether this consumer has any interop signals as dependencies.
139+ */
140+ hasInteropSignalDep : boolean ;
141+
146142 /**
147143 * Array of consumers of this producer that are "live" (they require push notifications).
148144 *
@@ -180,6 +176,11 @@ export interface ReactiveNode {
180176 */
181177 consumerOnSignalRead ( node : unknown ) : void ;
182178
179+ /**
180+ * Called when the signal is accessed.
181+ */
182+ producerOnAccess ?( ) : void ;
183+
183184 /**
184185 * Called when the signal becomes "live"
185186 */
@@ -211,27 +212,32 @@ interface ProducerNode extends ReactiveNode {
211212/**
212213 * Called by implementations when a producer's signal is read.
213214 */
214- export function producerAccessed ( node : ReactiveNode ) : void {
215+ export function producerAccessed < T > ( node : ReactiveNode & InteropSignal ) : void {
215216 if ( inNotificationPhase ) {
216217 throw new Error (
217218 typeof ngDevMode !== 'undefined' && ngDevMode
218219 ? `Assertion error: signal read during notification phase`
219220 : '' ,
220221 ) ;
221222 }
223+ getActiveConsumer ( ) ?. addProducer ( node ) ;
224+ }
222225
223- if ( activeConsumer === null ) {
224- // Accessed outside of a reactive context, so nothing to record.
225- return ;
226- }
227-
226+ export function internalProducerAccessed (
227+ node : ReactiveNode ,
228+ activeConsumer : ReactiveNode & InteropConsumer ,
229+ ) : void {
228230 activeConsumer . consumerOnSignalRead ( node ) ;
229231
230232 // This producer is the `idx`th dependency of `activeConsumer`.
231233 const idx = activeConsumer . nextProducerIndex ++ ;
232234
233235 assertConsumerNode ( activeConsumer ) ;
234236
237+ if ( node . hasInteropSignalDep ) {
238+ activeConsumer . hasInteropSignalDep = true ;
239+ }
240+
235241 if ( idx < activeConsumer . producerNode . length && activeConsumer . producerNode [ idx ] !== node ) {
236242 // There's been a change in producers since the last execution of `activeConsumer`.
237243 // `activeConsumer.producerNode[idx]` holds a stale dependency which will be be removed and
@@ -275,7 +281,7 @@ export function producerIncrementEpoch(): void {
275281 * Ensure this producer's `version` is up-to-date.
276282 */
277283export function producerUpdateValueVersion ( node : ReactiveNode ) : void {
278- if ( ! node . dirty && node . lastCleanEpoch === epoch ) {
284+ if ( ! node . dirty && node . lastCleanEpoch === epoch && ! node . hasInteropSignalDep ) {
279285 // Even non-live consumers can skip polling if they previously found themselves to be clean at
280286 // the current epoch, since their dependencies could not possibly have changed (such a change
281287 // would've increased the epoch).
@@ -324,7 +330,10 @@ export function producerNotifyConsumers(node: ReactiveNode): void {
324330 * based on the current consumer context.
325331 */
326332export function producerUpdatesAllowed ( ) : boolean {
327- return activeConsumer ?. consumerAllowSignalWrites !== false ;
333+ return (
334+ ( getActiveConsumer ( ) as ( InteropConsumer & ReactiveNode ) | null ) ?. consumerAllowSignalWrites !==
335+ false
336+ ) ;
328337}
329338
330339export function consumerMarkDirty ( node : ReactiveNode ) : void {
@@ -339,8 +348,13 @@ export function consumerMarkDirty(node: ReactiveNode): void {
339348 * Must be called by subclasses which represent reactive computations, before those computations
340349 * begin.
341350 */
342- export function consumerBeforeComputation ( node : ReactiveNode | null ) : ReactiveNode | null {
343- node && ( node . nextProducerIndex = 0 ) ;
351+ export function consumerBeforeComputation (
352+ node : ( ReactiveNode & InteropConsumer ) | null ,
353+ ) : InteropConsumer | null {
354+ if ( node ) {
355+ node . nextProducerIndex = 0 ;
356+ node . hasInteropSignalDep = false ;
357+ }
344358 return setActiveConsumer ( node ) ;
345359}
346360
@@ -352,7 +366,7 @@ export function consumerBeforeComputation(node: ReactiveNode | null): ReactiveNo
352366 */
353367export function consumerAfterComputation (
354368 node : ReactiveNode | null ,
355- prevConsumer : ReactiveNode | null ,
369+ prevConsumer : InteropConsumer | null ,
356370) : void {
357371 setActiveConsumer ( prevConsumer ) ;
358372
0 commit comments