Skip to content

Commit d2295c7

Browse files
brandonrobertsMikeRyanDev
authored andcommitted
feat(Store): Simplify API for adding meta-reducers (#87)
1 parent b9776ea commit d2295c7

File tree

9 files changed

+66
-13
lines changed

9 files changed

+66
-13
lines changed

docs/store/api.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,8 @@ export function getInitialState() {
4545

4646
## Reducer Factory
4747

48-
@ngrx/store composes your map of reducers into a single reducer. Use the `reducerFactory`
49-
configuration option to provide a composed action reducer factory:
48+
@ngrx/store composes your map of reducers into a single reducer. Use the `metaReducers`
49+
configuration option to provide an array of meta-reducers that are composed from right to left.
5050

5151
```ts
5252
import { StoreModule, combineReducers, compose } from '@ngrx/store';
@@ -62,11 +62,11 @@ function debug(reducer) {
6262
}
6363
}
6464

65-
const debugReducerFactory = compose(debug, combineReducers);
65+
const metaReducers = [debug];
6666

6767
@NgModule({
6868
imports: [
69-
StoreModule.forRoot(reducers, { reducerFactory: debugReducerFactory })
69+
StoreModule.forRoot(reducers, { metaReducers })
7070
]
7171
})
7272
export class AppModule {}
@@ -77,7 +77,7 @@ export class AppModule {}
7777
Store uses fractal state management, which provides state composition through feature modules,
7878
loaded eagerly or lazily. Provide feature states using the `StoreModule.forFeature` method. This
7979
method defines the name of the feature state and the reducers that make up the state. The same `initialState`
80-
and `reducerFactory` configuration options are available.
80+
and `metaReducers` configuration options are available.
8181

8282
```ts
8383
// feature.module.ts

example-app/app/app.module.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { CoreModule } from './core/core.module';
1616
import { AuthModule } from './auth/auth.module';
1717

1818
import { routes } from './routes';
19-
import { reducers } from './reducers';
19+
import { reducers, metaReducers } from './reducers';
2020
import { schema } from './db';
2121

2222
import { AppComponent } from './core/containers/app';
@@ -37,7 +37,7 @@ import { environment } from '../environments/environment';
3737
* meta-reducer. This returns all providers for an @ngrx/store
3838
* based application.
3939
*/
40-
StoreModule.forRoot(reducers),
40+
StoreModule.forRoot(reducers, { metaReducers }),
4141

4242
/**
4343
* @ngrx/router-store keeps router state up-to-date in the store.

example-app/app/reducers/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
ActionReducerMap,
33
createSelector,
44
createFeatureSelector,
5-
compose,
65
ActionReducer,
76
combineReducers,
87
Action,
@@ -37,6 +36,25 @@ export const reducers: ActionReducerMap<State> = {
3736
layout: fromLayout.reducer,
3837
};
3938

39+
// console.log all actions
40+
export function logger(reducer: ActionReducer<State>): ActionReducer<any, any> {
41+
return function(state: State, action: any): State {
42+
console.log('state', state);
43+
console.log('action', action);
44+
45+
return reducer(state, action);
46+
};
47+
}
48+
49+
/**
50+
* By default, @ngrx/store uses combineReducers with the reducer map to compose
51+
* the root meta-reducer. To add more meta-reducers, provide an array of meta-reducers
52+
* that will be composed to form the root meta-reducer.
53+
*/
54+
export const metaReducers: ActionReducer<any, any>[] = !environment.production
55+
? [logger]
56+
: [];
57+
4058
/**
4159
* Layout Reducers
4260
*/

modules/store/src/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ export {
77
} from './models';
88
export { StoreModule } from './store_module';
99
export { Store } from './store';
10-
export { combineReducers, compose } from './utils';
10+
export { combineReducers, compose, createReducerFactory } from './utils';
1111
export { ActionsSubject, INIT } from './actions_subject';
1212
export {
1313
ReducerManager,
@@ -24,10 +24,12 @@ export {
2424
export { State, StateObservable, reduceState } from './state';
2525
export {
2626
INITIAL_STATE,
27+
_REDUCER_FACTORY,
2728
REDUCER_FACTORY,
2829
INITIAL_REDUCERS,
2930
STORE_FEATURES,
3031
_INITIAL_STATE,
32+
META_REDUCERS,
3133
} from './tokens';
3234
export {
3335
StoreRootModule,

modules/store/src/models.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface StoreFeature<T, V extends Action = Action> {
2626
reducers: ActionReducerMap<T, V> | ActionReducer<T, V>;
2727
reducerFactory: ActionReducerFactory<T, V>;
2828
initialState?: InitialState<T>;
29+
metaReducers?: ActionReducer<any, any>[];
2930
}
3031

3132
export interface Selector<T, V> {

modules/store/src/reducer_manager.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
StoreFeature,
1010
} from './models';
1111
import { INITIAL_STATE, INITIAL_REDUCERS, REDUCER_FACTORY } from './tokens';
12-
import { omit } from './utils';
12+
import { omit, createReducerFactory } from './utils';
1313
import { ActionsSubject } from './actions_subject';
1414

1515
export abstract class ReducerObservable extends Observable<
@@ -34,13 +34,17 @@ export class ReducerManager extends BehaviorSubject<ActionReducer<any, any>>
3434
addFeature({
3535
reducers,
3636
reducerFactory,
37+
metaReducers,
3738
initialState,
3839
key,
3940
}: StoreFeature<any, any>) {
4041
const reducer =
4142
typeof reducers === 'function'
4243
? reducers
43-
: reducerFactory(reducers, initialState);
44+
: createReducerFactory(reducerFactory, metaReducers)(
45+
reducers,
46+
initialState
47+
);
4448

4549
this.addReducer(key, reducer);
4650
}

modules/store/src/store_module.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,15 @@ import {
1313
StoreFeature,
1414
InitialState,
1515
} from './models';
16-
import { combineReducers } from './utils';
16+
import { compose, combineReducers, createReducerFactory } from './utils';
1717
import {
1818
INITIAL_STATE,
1919
INITIAL_REDUCERS,
2020
REDUCER_FACTORY,
21+
_REDUCER_FACTORY,
2122
STORE_FEATURES,
2223
_INITIAL_STATE,
24+
META_REDUCERS,
2325
} from './tokens';
2426
import { ACTIONS_SUBJECT_PROVIDERS, ActionsSubject } from './actions_subject';
2527
import {
@@ -62,6 +64,7 @@ export class StoreFeatureModule implements OnDestroy {
6264
export type StoreConfig<T, V extends Action = Action> = {
6365
initialState?: InitialState<T>;
6466
reducerFactory?: ActionReducerFactory<T, V>;
67+
metaReducers?: ActionReducer<T, V>[];
6568
};
6669

6770
@NgModule({})
@@ -89,11 +92,20 @@ export class StoreModule {
8992
? { provide: INITIAL_REDUCERS, useExisting: reducers }
9093
: { provide: INITIAL_REDUCERS, useValue: reducers },
9194
{
92-
provide: REDUCER_FACTORY,
95+
provide: META_REDUCERS,
96+
useValue: config.metaReducers ? config.metaReducers : [],
97+
},
98+
{
99+
provide: _REDUCER_FACTORY,
93100
useValue: config.reducerFactory
94101
? config.reducerFactory
95102
: combineReducers,
96103
},
104+
{
105+
provide: REDUCER_FACTORY,
106+
deps: [_REDUCER_FACTORY, META_REDUCERS],
107+
useFactory: createReducerFactory,
108+
},
97109
ACTIONS_SUBJECT_PROVIDERS,
98110
REDUCER_MANAGER_PROVIDERS,
99111
SCANNED_ACTIONS_SUBJECT_PROVIDERS,
@@ -130,6 +142,7 @@ export class StoreModule {
130142
reducerFactory: config.reducerFactory
131143
? config.reducerFactory
132144
: combineReducers,
145+
metaReducers: config.metaReducers ? config.metaReducers : [],
133146
initialState: config.initialState,
134147
},
135148
},

modules/store/src/tokens.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@ import { OpaqueToken } from '@angular/core';
33
export const _INITIAL_STATE = new OpaqueToken('_ngrx/store Initial State');
44
export const INITIAL_STATE = new OpaqueToken('@ngrx/store Initial State');
55
export const REDUCER_FACTORY = new OpaqueToken('@ngrx/store Reducer Factory');
6+
export const _REDUCER_FACTORY = new OpaqueToken(
7+
'@ngrx/store Reducer Factory Provider'
8+
);
69
export const INITIAL_REDUCERS = new OpaqueToken('@ngrx/store Initial Reducers');
10+
export const META_REDUCERS = new OpaqueToken('@ngrx/store Meta Reducers');
711
export const STORE_FEATURES = new OpaqueToken('@ngrx/store Store Features');

modules/store/src/utils.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,14 @@ export function compose(...functions: any[]) {
8383
return rest.reduceRight((composed, fn) => fn(composed), last(arg));
8484
};
8585
}
86+
87+
export function createReducerFactory(
88+
reducerFactory: ActionReducerFactory<any, any>,
89+
metaReducers?: ActionReducer<any, any>[]
90+
): ActionReducerFactory<any, any> {
91+
if (Array.isArray(metaReducers) && metaReducers.length > 0) {
92+
return compose.apply(null, [...metaReducers, reducerFactory]);
93+
}
94+
95+
return reducerFactory;
96+
}

0 commit comments

Comments
 (0)