feat(signal-slice): optimize subscription management - #700
Conversation
|
View your CI Pipeline Execution ↗ for commit 753d88d
💡 Verify your cache is correct by running tasks in a sandbox. Read docs ↗ ☁️ Nx Cloud last updated this comment at |
There was a problem hiding this comment.
Code Review
This pull request optimizes subscription handling in signalSlice by removing redundant subscriptions to action sources and preventing multiple permanent subscription teardowns when actions are called with plain values. A unit test has been added to verify this behavior. The feedback suggests further cleanup by removing the now-redundant share() operator on the observable and restoring the Jest spy at the end of the new test to prevent mock pollution.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| destroyRef, | ||
| subject, | ||
| subs, | ||
| sharedObservable, | ||
| ); |
There was a problem hiding this comment.
Since sharedObservable is no longer passed to addReducerProperties, it is only subscribed to once by connect(state, sharedObservable) at line 204. This makes the share() operator on line 203 redundant. You can clean this up by removing const sharedObservable = observable.pipe(share()); and passing observable directly to connect(state, observable).
| state.increaseAge(1); | ||
| state.increaseAge(1); | ||
|
|
||
| expect(onDestroySpy.mock.calls.length).toEqual(baseline); |
Problem
Calling a function-form
actionSource(e.g.increaseAge: (state, $) => $.pipe(...)) with a plain, non-Observable value created a brand-new subscription to the action's already-connected, shared pipeline on every call, in addition to the one permanent subscriptionconnect()makes once at slice-creation time. Each of these extra subscriptions was wired totakeUntilDestroyed(destroyRef)and only released when the owning injector was destroyed — never between calls — so live subscriptions accumulated without bound for the lifetime of the slice.Root cause
The offending
.subscribe()call inaddReducerPropertiesused to have real handlers: it fed aneffectTriggerSubject that powered the experimentalactionEffectsAPI (added in #154). WhenactionEffectswas removed in #361, thenext/errorhandlers were stripped out but the bare.subscribe()call was left behind - a leftover with nothing listening, serving no purpose beyond registering a new teardown per call.Fix
Deleted the dead resubscription block and the now-unused
observableFromActionSourceparameter it depended on. State updates are unaffected - they were already handled entirely by the permanentconnect(state, sharedObservable)subscription made once at slice creation.Testing
DestroyRef.onDestroyteardowns beyond the one made at slice creation.signal-slicesuite (28 tests) andngxtensionlint pass.No public API changes.