Skip to content

Commit ca3c74a

Browse files
authored
feat(core): add injectable reducer factory (#4631)
1 parent 732c082 commit ca3c74a

6 files changed

Lines changed: 149 additions & 2 deletions

File tree

libs/core/state/src/reducers/create-meta.spec.ts renamed to libs/core/state/src/reducers/compose.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ActionReducer } from '@ngrx/store';
22

3-
import { daffComposeReducers } from './create-meta';
3+
import { daffComposeReducers } from './compose';
44

55
interface MockState {
66
foo: string;
File renamed without changes.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import { TestBed } from '@angular/core/testing';
2+
import {
3+
ActionReducer,
4+
MetaReducer,
5+
StoreConfig,
6+
StoreModule,
7+
} from '@ngrx/store';
8+
9+
import { daffComposeReducers } from './compose';
10+
import { createInjectableReducersTokens } from './injectable-reducers.factory';
11+
import { InjectableReducersTokens } from './injectable-reducers.type';
12+
13+
interface State {
14+
test: string;
15+
}
16+
17+
describe('@daffodil/core/state | createInjectableReducersTokens', () => {
18+
let metaReducers: Array<MetaReducer<State>>;
19+
let config: StoreConfig<State>;
20+
let tokens: InjectableReducersTokens<State>;
21+
let reducers: ActionReducer<State>;
22+
let extraReducer: ActionReducer<State>;
23+
let reducer: ActionReducer<State>;
24+
let result: State;
25+
26+
beforeEach(() => {
27+
const initialState: State = {
28+
test: 'test',
29+
};
30+
extraReducer = (state, action) => ({
31+
...state,
32+
test: `${state?.test} extra`,
33+
});
34+
metaReducers = [
35+
r => r,
36+
r => r,
37+
];
38+
reducers = daffComposeReducers([
39+
(state = initialState, action) => state,
40+
(state = initialState, action) => state,
41+
]);
42+
tokens = createInjectableReducersTokens<State>('TEST', () => reducers);
43+
44+
TestBed.configureTestingModule({
45+
imports: [
46+
StoreModule.forRoot(),
47+
StoreModule.forFeature('test', tokens.reducers.token, tokens.config.token),
48+
],
49+
providers: [
50+
...tokens.meta.provider(...metaReducers),
51+
...tokens.extra.provider(extraReducer),
52+
],
53+
});
54+
55+
reducer = TestBed.inject(tokens.reducers.token);
56+
config = TestBed.inject(tokens.config.token);
57+
result = reducer(initialState, { type: '' });
58+
});
59+
60+
it('should return the meta-reducers', () => {
61+
expect(TestBed.inject(tokens.config.token).metaReducers).toEqual(jasmine.arrayContaining(metaReducers));
62+
});
63+
64+
it('should run the extra reducer after the daffodil reducers', () => {
65+
expect(result.test).toEqual('test extra');
66+
});
67+
68+
it('should provide the extra reducers to the token', () => {
69+
expect(TestBed.inject(tokens.extra.token)).toContain(extraReducer);
70+
});
71+
72+
it('should provide the meta-reducers to the token', () => {
73+
metaReducers.forEach(metaReducer => {
74+
expect(TestBed.inject(tokens.meta.token)).toContain(metaReducer);
75+
});
76+
});
77+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {
2+
inject,
3+
Injector,
4+
runInInjectionContext,
5+
} from '@angular/core';
6+
import {
7+
ActionReducer,
8+
MetaReducer,
9+
StoreConfig,
10+
} from '@ngrx/store';
11+
12+
import {
13+
createMultiInjectionToken,
14+
createSingleInjectionToken,
15+
} from '@daffodil/core';
16+
17+
import { daffComposeReducers } from './compose';
18+
import { InjectableReducersTokens } from './injectable-reducers.type';
19+
20+
export const createInjectableReducersTokens = <T>(
21+
feature: string,
22+
createReducers: () => ActionReducer<T>,
23+
): InjectableReducersTokens<T> => {
24+
const extra = createMultiInjectionToken<ActionReducer<T>>(`${feature}_EXTRA_REDUCERS`);
25+
const reducers = createSingleInjectionToken<ActionReducer<T>>(
26+
`${feature}_REDUCERS`,
27+
{
28+
factory: () => daffComposeReducers([
29+
runInInjectionContext(inject(Injector), createReducers),
30+
...inject(extra.token),
31+
]),
32+
},
33+
);
34+
const meta = createMultiInjectionToken<MetaReducer<T>>(`${feature}_META_REDUCERS`);
35+
const config = createSingleInjectionToken<StoreConfig<T>>(
36+
`${feature}_STORE_CONFIG`,
37+
{
38+
providedIn: 'any',
39+
factory: () => ({
40+
metaReducers: inject(meta.token),
41+
}),
42+
},
43+
);
44+
45+
return {
46+
extra,
47+
reducers,
48+
meta,
49+
config,
50+
};
51+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import {
2+
ActionReducer,
3+
MetaReducer,
4+
StoreConfig,
5+
} from '@ngrx/store';
6+
7+
import {
8+
DaffMultiInjectionToken,
9+
DaffSingleInjectionToken,
10+
} from '@daffodil/core';
11+
12+
export interface InjectableReducersTokens<T> {
13+
extra: DaffMultiInjectionToken<ActionReducer<T>>;
14+
reducers: DaffSingleInjectionToken<ActionReducer<T>>;
15+
meta: DaffMultiInjectionToken<MetaReducer<T>>;
16+
config: DaffSingleInjectionToken<StoreConfig<T>>;
17+
}
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
export { daffComposeReducers } from './create-meta';
1+
export { daffComposeReducers } from './compose';
22
export { daffIdentityReducer } from './identity';
3+
export { createInjectableReducersTokens } from './injectable-reducers.factory';
4+
export { InjectableReducersTokens } from './injectable-reducers.type';

0 commit comments

Comments
 (0)