Skip to content

Commit

Permalink
feat(signals): rename rxMethod.unsubscribe to destroy (#4584)
Browse files Browse the repository at this point in the history
BREAKING CHANGES:

The `unsubscribe` method from `rxMethod` is renamed to `destroy`.

BEFORE:

const logNumber = rxMethod<number>(tap(console.log));

const num1Ref = logNumber(interval(1_000));
const num2Ref = logNumber(interval(2_000));

// destroy `num1Ref` after 2 seconds
setTimeout(() => num1Ref.unsubscribe(), 2_000);

// destroy all reactive method refs after 5 seconds
setTimeout(() => logNumber.unsubscribe(), 5_000);

AFTER:

const logNumber = rxMethod<number>(tap(console.log));

const num1Ref = logNumber(interval(1_000));
const num2Ref = logNumber(interval(2_000));

// destroy `num1Ref` after 2 seconds
setTimeout(() => num1Ref.destroy(), 2_000);

// destroy all reactive method refs after 5 seconds
setTimeout(() => logNumber.destroy(), 5_000);
  • Loading branch information
markostanimirovic authored Nov 13, 2024
1 parent de3ebce commit 57ad5c5
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 24 deletions.
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

0 comments on commit 57ad5c5

Please sign in to comment.