Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions modules/signals/rxjs-interop/spec/rx-method.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,22 +96,22 @@ describe('rxMethod', () => {
const subject$ = new Subject<number>();
const sig = signal(0);

const sub1 = method(subject$);
const sub2 = method(sig);
const ref1 = method(subject$);
const ref2 = method(sig);
expect(results).toEqual([]);

subject$.next(1);
sig.set(1);
TestBed.flushEffects();
expect(results).toEqual([1, 1]);

sub1.unsubscribe();
ref1.destroy();
subject$.next(2);
sig.set(2);
TestBed.flushEffects();
expect(results).toEqual([1, 1, 2]);

sub2.unsubscribe();
ref2.destroy();
sig.set(3);
TestBed.flushEffects();
expect(results).toEqual([1, 1, 2]);
Expand All @@ -138,7 +138,7 @@ describe('rxMethod', () => {
method(1);
expect(results).toEqual([1, 1, 1]);

method.unsubscribe();
method.destroy();
expect(destroyed).toBe(true);

subject1$.next(2);
Expand Down
23 changes: 13 additions & 10 deletions modules/signals/rxjs-interop/src/rx-method.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ import {
Signal,
untracked,
} from '@angular/core';
import { isObservable, noop, Observable, Subject, Unsubscribable } from 'rxjs';
import { isObservable, noop, Observable, Subject } from 'rxjs';

type RxMethodRef = {
destroy: () => void;
};

type RxMethod<Input> = ((
input: Input | Signal<Input> | Observable<Input>,
config?: { injector?: Injector }
) => Unsubscribable) &
Unsubscribable;
) => RxMethodRef) &
RxMethodRef;

export function rxMethod<Input>(
generator: (source$: Observable<Input>) => Observable<unknown>,
Expand All @@ -32,10 +36,10 @@ export function rxMethod<Input>(
const rxMethodFn = (
input: Input | Signal<Input> | Observable<Input>,
config?: { injector?: Injector }
) => {
): RxMethodRef => {
if (isStatic(input)) {
source$.next(input);
return { unsubscribe: noop };
return { destroy: noop };
}

const instanceInjector =
Expand All @@ -49,10 +53,9 @@ export function rxMethod<Input>(
},
{ injector: instanceInjector }
);
const instanceSub = { unsubscribe: () => watcher.destroy() };
sourceSub.add(instanceSub);
sourceSub.add({ unsubscribe: () => watcher.destroy() });

return instanceSub;
return watcher;
}

const instanceSub = input.subscribe((value) => source$.next(value));
Expand All @@ -64,9 +67,9 @@ export function rxMethod<Input>(
.onDestroy(() => instanceSub.unsubscribe());
}

return instanceSub;
return { destroy: () => instanceSub.unsubscribe() };
};
rxMethodFn.unsubscribe = sourceSub.unsubscribe.bind(sourceSub);
rxMethodFn.destroy = sourceSub.unsubscribe.bind(sourceSub);

return rxMethodFn;
}
Expand Down
18 changes: 9 additions & 9 deletions projects/ngrx.io/content/guide/signals/rxjs-integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ If the injector is not provided when calling the reactive method outside of curr

### Manual Cleanup

If a reactive method needs to be cleaned up before the injector is destroyed, manual cleanup can be performed by calling the `unsubscribe` method.
If a reactive method needs to be cleaned up before the injector is destroyed, manual cleanup can be performed by calling the `destroy` method.

```ts
import { Component, OnInit } from '@angular/core';
Expand All @@ -266,15 +266,15 @@ export class NumbersComponent implements OnInit {
this.logNumber(num2$);

setTimeout(() => {
// 👇 Clean up all reactive method subscriptions after 3 seconds.
this.logNumber.unsubscribe();
// 👇 Destroy the reactive method after 3 seconds.
this.logNumber.destroy();
}, 3_000);
}
}
```

When invoked, the reactive method returns a subscription.
Using this subscription allows manual unsubscribing from a specific call, preserving the activity of other reactive method calls until the corresponding injector is destroyed.
When invoked, the reactive method returns the object with the `destroy` method.
This allows manual cleanup of a specific call, preserving the activity of other reactive method calls until the corresponding injector is destroyed.

```ts
import { Component, OnInit } from '@angular/core';
Expand All @@ -289,12 +289,12 @@ export class NumbersComponent implements OnInit {
const num1$ = interval(500);
const num2$ = interval(1_000);

const num1Sub = this.logNumber(num1$);
this.logNumber(num2$);
const num1Ref = this.logNumber(num1$);
const num2Ref = this.logNumber(num2$);

setTimeout(() => {
// 👇 Clean up the first reactive method subscription after 2 seconds.
num1Sub.unsubscribe();
// 👇 Destroy the first reactive method call after 2 seconds.
num1Ref.destroy();
}, 2_000);
}
}
Expand Down