Skip to content

Commit bcd1702

Browse files
committed
feat(effects): enhance options to pass a DestroyRef
1 parent 45c9ba3 commit bcd1702

File tree

5 files changed

+159
-69
lines changed

5 files changed

+159
-69
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ A set of 🔥 Angular packages tools.
1111

1212
- [stream](./libs/stream/README.md) - the better async pipe
1313
- [rx](./libs/rx/README.md) - reactive tools
14-
- [cdk](./libs/cdk/README.md) - common tools
14+
- [effects](./libs/effects/README.md) - modern util to manage subscriptions with zero-boilerplate code
1515
- [rx-hooks](./libs/rx-hooks/README.md) - reactive lifecycle hooks
1616
- [rx-stateful](./libs/rx-stateful/README.md) - a powerful stateful wrapper for synchronous and asynchronous observables
1717
- [immutable-helper](./libs/immutable-helper/README.md) - immutable helper functions to deal with objects and arrays

libs/effects/README.md

Lines changed: 87 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,90 @@
1-
# effects
1+
# @angular-kit/effects
2+
A modern toolkit using the latest Angular features to effectively manage your effects/subscriptions with zero-boilerplate code needed.
23

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

5-
## Running unit tests
6+
## Included
7+
* `effects`
68

7-
Run `nx test effects` to execute the unit tests.
9+
### `effects`
10+
11+
`effects` is a convenience function which acts as a container to take care of
12+
multiple subscriptions and execute side effects. It will **automatically** and **safely** unsubscribe from
13+
any observable.
14+
15+
#### Usage
16+
*Note* that you need to use `effects` within an injection context. If you want to
17+
use it outside an injection context you can pass the `Ìnjector` as argument.
18+
19+
```ts
20+
const eff = effects(({run, runOnCleanUp}) => {
21+
run(interval(1000), v => console.log(v))
22+
// register more effects
23+
runOnCleanUp(() => {
24+
// e.g. save something to local storage
25+
})
26+
})
27+
```
28+
29+
You also get fine-grained hooks to run code on clean-up of a single effect:
30+
31+
```ts
32+
const eff = effects();
33+
const intervalEffect = eff.run(interval(1000), v => console.log(v), {
34+
onCleanUp: () => {
35+
// e.g. save something to local storage
36+
}
37+
});
38+
```
39+
40+
The `onCleanUp`-function will be executed either when `intervalEffect.cleanUp()` is called or when
41+
the `effects`-instance is destroyed. **Note** the `onCleanUp`-function will run only **once**.
42+
43+
#### Comparison
44+
45+
###### Without `effects` pre Angular 16
46+
There are several well-established patterns to handle subscriptions in Angular. This example uses plain
47+
subscriptions and unsubscribes in the `ngOnDestroy` lifecycle hook.
48+
```ts
49+
@Component(...)
50+
export class AppComponent implements OnDestroy {
51+
private subscription: Subscription;
52+
53+
constructor() {
54+
this.subscription = interval(1000).subscribe(v => console.log(v));
55+
}
56+
57+
ngOnDestroy() {
58+
this.subscription.unsubscribe();
59+
}
60+
}
61+
```
62+
63+
###### Without `effects` post Angular 16
64+
There are several well-established patterns to handle subscriptions in Angular. This example uses plain
65+
subscriptions and unsubscribes in the `ngOnDestroy` lifecycle hook.
66+
```ts
67+
@Component(...)
68+
export class AppComponent {
69+
private subscription: Subscription;
70+
71+
constructor() {
72+
this.subscription = interval(1000).subscribe(v => console.log(v));
73+
inject(DestroyRef).onDestroy(() => this.subscription.unsubscribe());
74+
}
75+
}
76+
```
77+
###### With `effects`
78+
```ts
79+
@Component(...)
80+
export class AppComponent {
81+
private eff = effects();
82+
83+
constructor() {
84+
this.eff.run(interval(1000).subscribe(v => console.log(v));
85+
}
86+
}
87+
```
88+
89+
# Version compatibility
90+
`@angular-kit/effects` is compatible with Angular versions 16 and above and it requires RxJs >= 7.0.0.

libs/effects/package.json

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
{
22
"name": "@angular-kit/effects",
33
"version": "0.0.1",
4+
"publishConfig": {
5+
"access": "public"
6+
},
47
"peerDependencies": {
5-
"@angular/common": "^17.3.0",
6-
"@angular/core": "^17.3.0"
8+
"@angular/common": ">=16.0.0",
9+
"@angular/core": ">=16.0.0",
10+
"rxjs": ">=7.0.0"
711
},
812
"dependencies": {
913
"tslib": "^2.3.0"
1014
},
11-
"sideEffects": false
15+
"sideEffects": false,
16+
"license": "MIT",
17+
"description": "Powerful tools to manage observable subscriptions with zero-boilerplate",
18+
"keywords": [
19+
"angular",
20+
"rxjs",
21+
"observables",
22+
"subscriptions",
23+
"memory-leak"
24+
],
25+
"author": "Michael Berger",
26+
"homepage": "https://github.com/code-workers-io/angular-kit/libs/effects",
27+
"repository": {
28+
"type": "git",
29+
"url": "https://github.com/mikelgo/angular-kit/tree/main/libs/effects"
30+
}
1231
}

libs/effects/src/lib/effects.spec.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,17 +112,17 @@ describe(effects.name, () => {
112112
const cleanUpSpy3 = jest.fn();
113113

114114
const effect = effects.run(trigger$$, (v) => service.triggerEffect(v), {
115-
cleanUp: cleanUpSpy,
115+
onCleanUp: cleanUpSpy,
116116
});
117117
const effect2 = effects.run(
118118
trigger$$.subscribe((v) => service.triggerEffect(v)),
119119
{
120-
cleanUp: cleanUpSpy2,
120+
onCleanUp: cleanUpSpy2,
121121
}
122122
);
123123

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

128128
trigger$$.next(10);
@@ -146,16 +146,16 @@ describe(effects.name, () => {
146146
const effect = effects.run(
147147
trigger$$.subscribe((v) => service.triggerEffect(v)),
148148
{
149-
cleanUp: cleanUpSpy,
149+
onCleanUp: cleanUpSpy,
150150
}
151151
);
152152

153153
const effect2 = effects.run(trigger$$, (v) => service.triggerEffect(v), {
154-
cleanUp: cleanUpSpy2,
154+
onCleanUp: cleanUpSpy2,
155155
});
156156

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

161161
trigger$$.next(10);
@@ -178,15 +178,15 @@ describe(effects.name, () => {
178178
const effect = effects.run(
179179
trigger$$.subscribe((v) => service.triggerEffect(v)),
180180
{
181-
cleanUp: cleanUpSpy,
181+
onCleanUp: cleanUpSpy,
182182
}
183183
);
184184

185185
const effect2 = effects.run(trigger$$, (v) => service.triggerEffect(v), {
186-
cleanUp: cleanUpSpy2,
186+
onCleanUp: cleanUpSpy2,
187187
});
188188
const effect3 = effects.run(trigger$$.pipe(tap((v) => service.triggerEffect(v))), {
189-
cleanUp: cleanUpSpy3,
189+
onCleanUp: cleanUpSpy3,
190190
});
191191

192192
trigger$$.next(10);
@@ -277,15 +277,15 @@ class TestComponent implements OnDestroy {
277277
source$ = of(10);
278278
clearInterval$$ = new Subject();
279279

280-
effects = effects(({ run, runOnInstanceDestroy }) => {
280+
effects = effects(({ run, runOnCleanUp }) => {
281281
run(this.source$, (v) => this.service.sourceEffect(v));
282282
const triggerEffect = run(this.trigger$$, (v) => this.service.triggerEffect(v));
283283

284284
run(this.unsubscribeTrigger$$, () => {
285285
triggerEffect.cleanUp();
286286
});
287287

288-
runOnInstanceDestroy(() => {
288+
runOnCleanUp(() => {
289289
this.service.teardownEffect();
290290
this.clearInterval$$.next(void 0);
291291
});

0 commit comments

Comments
 (0)