Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 94d3cc4

Browse files
BioPhotonhoebbelsB
authored andcommitted
refactor(state): reimplement reduced effects
1 parent 4c403c3 commit 94d3cc4

File tree

8 files changed

+315
-127
lines changed

8 files changed

+315
-127
lines changed

libs/state/effects/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { RxEffects } from './lib/effects.service';
2-
export { rxEffects } from './lib/effects';
2+
export { rxEffects } from './lib/rx-effects';
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { rxEffects } from '@rx-angular/state/effects';
2+
import { Component, inject, Injectable, InjectionToken } from '@angular/core';
3+
import { of, timer } from 'rxjs';
4+
import { TestBed } from '@angular/core/testing';
5+
import { wait } from 'nx-cloud/lib/utilities/waiter';
6+
7+
type Movie = {};
8+
9+
@Injectable({ providedIn: 'root' })
10+
export class LocalStorage {
11+
items = {};
12+
setItem(prop: string, value: string) {
13+
this.items[prop] = value;
14+
}
15+
removeItem(prop: string) {
16+
delete this.items[prop];
17+
}
18+
getItem(prop: string) {
19+
return this.items[prop];
20+
}
21+
}
22+
23+
const BackupInterval = new InjectionToken<number>('BackupInterval');
24+
25+
@Component({
26+
template: ` <input name="title" [(ngModel)]="movie" />
27+
<button name="save" (click)="save()">Save</button>`,
28+
providers: [{ provide: BackupInterval, useValue: 40 }],
29+
})
30+
class ListComponent {
31+
protected movie = '';
32+
private backupInterval = inject(BackupInterval);
33+
private localStorage = inject(LocalStorage);
34+
35+
private ef = rxEffects(({ register }) => {
36+
const updateBackup = () =>
37+
this.localStorage.setItem('editName', this.movie);
38+
register(timer(0, this.backupInterval), updateBackup);
39+
});
40+
41+
save() {
42+
localStorage.removeItem('editName');
43+
}
44+
45+
ngOnInit() {
46+
this.effects.register(this.util.rotationChanged$, () => {
47+
console.log('viewport rotation changed');
48+
});
49+
}
50+
}
51+
52+
// Test helper code ==========
53+
54+
function setupComponent() {
55+
TestBed.configureTestingModule({
56+
declarations: [ListComponent],
57+
providers: [{ provide: BackupInterval, useValue: 10 }],
58+
});
59+
60+
const localStorage = TestBed.inject(LocalStorage);
61+
62+
const fixture = TestBed.createComponent(ListComponent);
63+
const component = fixture.componentInstance;
64+
65+
const searchInputElem: HTMLInputElement = fixture.nativeElement.querySelector(
66+
'input[name="search"]'
67+
);
68+
const searchInputChange = (value: string) => {
69+
searchInputElem.value = value;
70+
searchInputElem.dispatchEvent(new Event('change'));
71+
};
72+
73+
return { fixture, component, localStorage, searchInputChange };
74+
}
75+
76+
describe('effects usage in a component', () => {
77+
afterEach(() => {
78+
jest.restoreAllMocks();
79+
});
80+
81+
test('should ', async () => {
82+
const { component, fixture, localStorage, searchInputChange } =
83+
setupComponent();
84+
85+
const spySetItem = jest.spyOn(localStorage, 'setItem');
86+
const spyRemoveItem = jest.spyOn(localStorage, 'removeItem');
87+
88+
expect(spySetItem).toBeCalledTimes(0);
89+
await wait(200);
90+
expect(spySetItem).toBeCalledTimes(1);
91+
expect(spySetItem).toBeCalledWith('');
92+
93+
expect(spySetItem).toBeCalledTimes(1);
94+
expect(spySetItem).toBeCalledWith(1);
95+
});
96+
});
97+
98+
function setupComponent2() {
99+
TestBed.configureTestingModule({
100+
declarations: [ListComponent],
101+
providers: [{ provide: BackupInterval, useValue: 10 }],
102+
});
103+
104+
const localStorage = TestBed.inject(LocalStorage);
105+
106+
const fixture = TestBed.createComponent(ListComponent);
107+
const component = fixture.componentInstance;
108+
109+
const saveButtonElem: HTMLInputElement = fixture.nativeElement.querySelector(
110+
'button[name="save"]'
111+
);
112+
const saveButtonClick = () => {
113+
saveButtonElem.dispatchEvent(new Event('change'));
114+
};
115+
116+
return { fixture, component, localStorage };
117+
}

libs/state/effects/spec/effects-funtion.spec.ts renamed to libs/state/effects/src/lib/effects-funtion.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Component } from '@angular/core';
22
import { TestBed } from '@angular/core/testing';
33
import { RxEffects } from '@rx-angular/state/effects';
44
import { of, tap } from 'rxjs';
5-
import { rxEffects, RxEffectsSetupFn } from '../src/lib/effects';
5+
import { rxEffects, RxEffectsSetupFn } from './effects';
66

77
describe(rxEffects, () => {
88
it('should create RxEffects instance', () => {

libs/state/effects/spec/effects.service.spec.ts renamed to libs/state/effects/src/lib/effects.service.spec.ts

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Component, ErrorHandler } from '@angular/core';
22
import { TestBed } from '@angular/core/testing';
33
import { BehaviorSubject, EMPTY, Observable, throwError, map, tap } from 'rxjs';
4-
import { RxEffects } from './../src/lib/effects.service';
4+
import { RxEffects } from './effects.service';
55

66
// tslint:disable: max-classes-per-file
77

@@ -21,29 +21,23 @@ const selector3 = (state: TState) => `${state}3`;
2121
const selector4 = (state: TState) => `${state}4`;
2222

2323
class Service {
24-
method1(..._: any[]): void {
25-
}
24+
method1(..._: any[]): void {}
2625

27-
method2(..._: any[]): void {
28-
}
26+
method2(..._: any[]): void {}
2927

30-
method3(..._: any[]): void {
31-
}
28+
method3(..._: any[]): void {}
3229

33-
method4(..._: any[]): void {
34-
}
30+
method4(..._: any[]): void {}
3531

36-
method4OnError(..._: any[]): void {
37-
}
32+
method4OnError(..._: any[]): void {}
3833

39-
method4OnComplete(..._: any[]): void {
40-
}
34+
method4OnComplete(..._: any[]): void {}
4135
}
4236

4337
// tslint:disable-next-line: prefer-on-push-component-change-detection use-component-selector
4438
@Component({
4539
template: '',
46-
providers: [RxEffects]
40+
providers: [RxEffects],
4741
})
4842
class TestComponent {
4943
constructor(store: Store, service: Service, effects: RxEffects) {
@@ -53,30 +47,32 @@ class TestComponent {
5347
effects.register(store.select(selector4), {
5448
next: service.method4,
5549
error: service.method4OnError,
56-
complete: service.method4OnComplete
50+
complete: service.method4OnComplete,
5751
});
5852
}
5953
}
6054

6155
// tslint:disable-next-line: prefer-on-push-component-change-detection use-component-selector
6256
@Component({
6357
template: '',
64-
providers: [RxEffects]
58+
providers: [RxEffects],
6559
})
6660
class TestUntilEffectComponent {
6761
constructor(store: Store, service: Service, private effects: RxEffects) {
68-
const effectId1 = effects.register(store.select((v) => v === 'effectTrigger'), () => void 0);
69-
store.state$.pipe(
70-
effects.untilEffect(effectId1)
71-
).subscribe(service.method1);
72-
62+
const effectId1 = effects.register(
63+
store.select((v) => v === 'effectTrigger'),
64+
() => void 0
65+
);
66+
store.state$
67+
.pipe(effects.untilEffect(effectId1))
68+
.subscribe(service.method1);
7369
}
7470
}
7571

7672
// tslint:disable-next-line: prefer-on-push-component-change-detection use-component-selector
7773
@Component({
7874
template: '',
79-
providers: [RxEffects]
75+
providers: [RxEffects],
8076
})
8177
class TestOnDestroyComponent {
8278
constructor(store: Store, service: Service, private effects: RxEffects) {
@@ -87,7 +83,7 @@ class TestOnDestroyComponent {
8783
// tslint:disable-next-line: prefer-on-push-component-change-detection use-component-selector
8884
@Component({
8985
template: '',
90-
providers: [RxEffects]
86+
providers: [RxEffects],
9187
})
9288
class TestUnregisterComponent {
9389
private readonly effectId1: number;
@@ -123,11 +119,11 @@ describe('RxEffects', () => {
123119
method1: jest.fn(),
124120
method2: jest.fn(),
125121
method3: jest.fn(),
126-
method4: jest.fn()
122+
method4: jest.fn(),
127123
};
128124
await TestBed.configureTestingModule({
129125
declarations: [TestComponent],
130-
providers: [Store, { provide: Service, useValue: service }]
126+
providers: [Store, { provide: Service, useValue: service }],
131127
}).compileComponents();
132128
TestBed.createComponent(TestComponent);
133129
const store = TestBed.inject(Store);
@@ -152,11 +148,11 @@ describe('RxEffects', () => {
152148
method1: jest.fn(),
153149
method2: jest.fn(),
154150
method3: jest.fn(),
155-
method4: jest.fn()
151+
method4: jest.fn(),
156152
};
157153
await TestBed.configureTestingModule({
158154
declarations: [TestComponent],
159-
providers: [Store, { provide: Service, useValue: service }]
155+
providers: [Store, { provide: Service, useValue: service }],
160156
}).compileComponents();
161157
const fixture = TestBed.createComponent(TestComponent);
162158
const store = TestBed.inject(Store);
@@ -190,10 +186,10 @@ describe('RxEffects', () => {
190186
},
191187
method2: jest.fn(),
192188
method3: jest.fn(),
193-
method4: jest.fn()
189+
method4: jest.fn(),
194190
};
195191
const customErrorHandler: ErrorHandler = {
196-
handleError: jest.fn()
192+
handleError: jest.fn(),
197193
};
198194
await TestBed.configureTestingModule({
199195
declarations: [TestComponent],
@@ -202,9 +198,9 @@ describe('RxEffects', () => {
202198
{ provide: Service, useValue: service },
203199
{
204200
provide: ErrorHandler,
205-
useValue: customErrorHandler
206-
}
207-
]
201+
useValue: customErrorHandler,
202+
},
203+
],
208204
}).compileComponents();
209205
TestBed.createComponent(TestComponent);
210206
const store = TestBed.inject(Store);
@@ -222,19 +218,15 @@ describe('RxEffects', () => {
222218

223219
test('should invoke complete callback upon completion', async () => {
224220
const service = {
225-
method1: () => {
226-
},
227-
method2: () => {
228-
},
229-
method3: () => {
230-
},
231-
method4: () => {
232-
},
233-
method4OnComplete: jest.fn()
221+
method1: () => {},
222+
method2: () => {},
223+
method3: () => {},
224+
method4: () => {},
225+
method4OnComplete: jest.fn(),
234226
};
235227
await TestBed.configureTestingModule({
236228
declarations: [TestComponent],
237-
providers: [Store, { provide: Service, useValue: service }]
229+
providers: [Store, { provide: Service, useValue: service }],
238230
}).compileComponents();
239231
TestBed.createComponent(TestComponent);
240232
const store = TestBed.inject(Store);
@@ -251,15 +243,11 @@ describe('RxEffects', () => {
251243

252244
test('should invoke error callback when source observable errors', async () => {
253245
const service = {
254-
method1: () => {
255-
},
256-
method2: () => {
257-
},
258-
method3: () => {
259-
},
260-
method4: () => {
261-
},
262-
method4OnError: jest.fn()
246+
method1: () => {},
247+
method2: () => {},
248+
method3: () => {},
249+
method4: () => {},
250+
method4OnError: jest.fn(),
263251
};
264252
jest
265253
.spyOn(Store.prototype, 'select')
@@ -274,12 +262,12 @@ describe('RxEffects', () => {
274262
Store,
275263
{ provide: Service, useValue: service },
276264
{
277-
provide: ErrorHandler, useValue: {
278-
handleError: () => {
279-
}
280-
}
281-
}
282-
]
265+
provide: ErrorHandler,
266+
useValue: {
267+
handleError: () => {},
268+
},
269+
},
270+
],
283271
}).compileComponents();
284272
TestBed.createComponent(TestComponent);
285273
const store = TestBed.inject(Store);
@@ -294,11 +282,11 @@ describe('RxEffects', () => {
294282
test('should cancel side-effect if unregistered imperatively', async () => {
295283
const service = {
296284
method1: jest.fn(),
297-
method2: jest.fn()
285+
method2: jest.fn(),
298286
};
299287
await TestBed.configureTestingModule({
300288
declarations: [TestUnregisterComponent],
301-
providers: [Store, { provide: Service, useValue: service }]
289+
providers: [Store, { provide: Service, useValue: service }],
302290
}).compileComponents();
303291
const fixture = TestBed.createComponent(TestUnregisterComponent);
304292
const component = fixture.componentInstance;
@@ -332,7 +320,7 @@ describe('RxEffects', () => {
332320
};
333321
await TestBed.configureTestingModule({
334322
declarations: [TestUntilEffectComponent],
335-
providers: [Store, { provide: Service, useValue: service }]
323+
providers: [Store, { provide: Service, useValue: service }],
336324
}).compileComponents();
337325
const fixture = TestBed.createComponent(TestUntilEffectComponent);
338326
const component = fixture.componentInstance;
@@ -350,7 +338,6 @@ describe('RxEffects', () => {
350338
store.state$.next('foo');
351339

352340
expect(service.method1).not.toHaveBeenCalled();
353-
354341
});
355342

356343
test('should cancel side-effect if components gets destroyed when using onDestroy', async () => {
@@ -359,7 +346,7 @@ describe('RxEffects', () => {
359346
};
360347
await TestBed.configureTestingModule({
361348
declarations: [TestOnDestroyComponent],
362-
providers: [Store, { provide: Service, useValue: service }]
349+
providers: [Store, { provide: Service, useValue: service }],
363350
}).compileComponents();
364351
const fixture = TestBed.createComponent(TestOnDestroyComponent);
365352
const component = fixture.componentInstance;

libs/state/effects/src/lib/effects.service.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import { DestroyProp, OnDestroy$ } from './model';
2222
import { toHook, untilDestroyed } from './utils';
2323

2424
/**
25+
* @deprecated - use rxEffects instead
26+
*
2527
* Reduces subscription boilerplate for performing observable-based side-effects in components.
2628
*
2729
* Before:

0 commit comments

Comments
 (0)