Skip to content

Commit

Permalink
feat(effects): enhance options to pass a DestroyRef
Browse files Browse the repository at this point in the history
  • Loading branch information
michaelbe812 committed May 21, 2024
1 parent 45c9ba3 commit bcd1702
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 69 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A set of 🔥 Angular packages tools.

- [stream](./libs/stream/README.md) - the better async pipe
- [rx](./libs/rx/README.md) - reactive tools
- [cdk](./libs/cdk/README.md) - common tools
- [effects](./libs/effects/README.md) - modern util to manage subscriptions with zero-boilerplate code
- [rx-hooks](./libs/rx-hooks/README.md) - reactive lifecycle hooks
- [rx-stateful](./libs/rx-stateful/README.md) - a powerful stateful wrapper for synchronous and asynchronous observables
- [immutable-helper](./libs/immutable-helper/README.md) - immutable helper functions to deal with objects and arrays
Expand Down
91 changes: 87 additions & 4 deletions libs/effects/README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,90 @@
# effects
# @angular-kit/effects
A modern toolkit using the latest Angular features to effectively manage your effects/subscriptions with zero-boilerplate code needed.

This library was generated with [Nx](https://nx.dev).
Tooling to handle your effects (subscriptions)!

## Running unit tests
## Included
* `effects`

Run `nx test effects` to execute the unit tests.
### `effects`

`effects` is a convenience function which acts as a container to take care of
multiple subscriptions and execute side effects. It will **automatically** and **safely** unsubscribe from
any observable.

#### Usage
*Note* that you need to use `effects` within an injection context. If you want to
use it outside an injection context you can pass the `Ìnjector` as argument.

```ts
const eff = effects(({run, runOnCleanUp}) => {
run(interval(1000), v => console.log(v))
// register more effects
runOnCleanUp(() => {
// e.g. save something to local storage
})
})
```

You also get fine-grained hooks to run code on clean-up of a single effect:

```ts
const eff = effects();
const intervalEffect = eff.run(interval(1000), v => console.log(v), {
onCleanUp: () => {
// e.g. save something to local storage
}
});
```

The `onCleanUp`-function will be executed either when `intervalEffect.cleanUp()` is called or when
the `effects`-instance is destroyed. **Note** the `onCleanUp`-function will run only **once**.

#### Comparison

###### Without `effects` pre Angular 16
There are several well-established patterns to handle subscriptions in Angular. This example uses plain
subscriptions and unsubscribes in the `ngOnDestroy` lifecycle hook.
```ts
@Component(...)
export class AppComponent implements OnDestroy {
private subscription: Subscription;

constructor() {
this.subscription = interval(1000).subscribe(v => console.log(v));
}

ngOnDestroy() {
this.subscription.unsubscribe();
}
}
```

###### Without `effects` post Angular 16
There are several well-established patterns to handle subscriptions in Angular. This example uses plain
subscriptions and unsubscribes in the `ngOnDestroy` lifecycle hook.
```ts
@Component(...)
export class AppComponent {
private subscription: Subscription;

constructor() {
this.subscription = interval(1000).subscribe(v => console.log(v));
inject(DestroyRef).onDestroy(() => this.subscription.unsubscribe());
}
}
```
###### With `effects`
```ts
@Component(...)
export class AppComponent {
private eff = effects();

constructor() {
this.eff.run(interval(1000).subscribe(v => console.log(v));
}
}
```
# Version compatibility
`@angular-kit/effects` is compatible with Angular versions 16 and above and it requires RxJs >= 7.0.0.
25 changes: 22 additions & 3 deletions libs/effects/package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
{
"name": "@angular-kit/effects",
"version": "0.0.1",
"publishConfig": {
"access": "public"
},
"peerDependencies": {
"@angular/common": "^17.3.0",
"@angular/core": "^17.3.0"
"@angular/common": ">=16.0.0",
"@angular/core": ">=16.0.0",
"rxjs": ">=7.0.0"
},
"dependencies": {
"tslib": "^2.3.0"
},
"sideEffects": false
"sideEffects": false,
"license": "MIT",
"description": "Powerful tools to manage observable subscriptions with zero-boilerplate",
"keywords": [
"angular",
"rxjs",
"observables",
"subscriptions",
"memory-leak"
],
"author": "Michael Berger",
"homepage": "https://github.com/code-workers-io/angular-kit/libs/effects",
"repository": {
"type": "git",
"url": "https://github.com/mikelgo/angular-kit/tree/main/libs/effects"
}
}
22 changes: 11 additions & 11 deletions libs/effects/src/lib/effects.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,17 +112,17 @@ describe(effects.name, () => {
const cleanUpSpy3 = jest.fn();

const effect = effects.run(trigger$$, (v) => service.triggerEffect(v), {
cleanUp: cleanUpSpy,
onCleanUp: cleanUpSpy,
});
const effect2 = effects.run(
trigger$$.subscribe((v) => service.triggerEffect(v)),
{
cleanUp: cleanUpSpy2,
onCleanUp: cleanUpSpy2,
}
);

const effect3 = effects.run(trigger$$.pipe(tap((v) => service.triggerEffect(v))), {
cleanUp: cleanUpSpy3,
onCleanUp: cleanUpSpy3,
});

trigger$$.next(10);
Expand All @@ -146,16 +146,16 @@ describe(effects.name, () => {
const effect = effects.run(
trigger$$.subscribe((v) => service.triggerEffect(v)),
{
cleanUp: cleanUpSpy,
onCleanUp: cleanUpSpy,
}
);

const effect2 = effects.run(trigger$$, (v) => service.triggerEffect(v), {
cleanUp: cleanUpSpy2,
onCleanUp: cleanUpSpy2,
});

const effect3 = effects.run(trigger$$.pipe(tap((v) => service.triggerEffect(v))), {
cleanUp: cleanUpSpy3,
onCleanUp: cleanUpSpy3,
});

trigger$$.next(10);
Expand All @@ -178,15 +178,15 @@ describe(effects.name, () => {
const effect = effects.run(
trigger$$.subscribe((v) => service.triggerEffect(v)),
{
cleanUp: cleanUpSpy,
onCleanUp: cleanUpSpy,
}
);

const effect2 = effects.run(trigger$$, (v) => service.triggerEffect(v), {
cleanUp: cleanUpSpy2,
onCleanUp: cleanUpSpy2,
});
const effect3 = effects.run(trigger$$.pipe(tap((v) => service.triggerEffect(v))), {
cleanUp: cleanUpSpy3,
onCleanUp: cleanUpSpy3,
});

trigger$$.next(10);
Expand Down Expand Up @@ -277,15 +277,15 @@ class TestComponent implements OnDestroy {
source$ = of(10);
clearInterval$$ = new Subject();

effects = effects(({ run, runOnInstanceDestroy }) => {
effects = effects(({ run, runOnCleanUp }) => {
run(this.source$, (v) => this.service.sourceEffect(v));
const triggerEffect = run(this.trigger$$, (v) => this.service.triggerEffect(v));

run(this.unsubscribeTrigger$$, () => {
triggerEffect.cleanUp();
});

runOnInstanceDestroy(() => {
runOnCleanUp(() => {
this.service.teardownEffect();
this.clearInterval$$.next(void 0);
});
Expand Down
Loading

0 comments on commit bcd1702

Please sign in to comment.