Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(Store): Added simplified API for adding meta-reducers #87

Merged
merged 1 commit into from
Jul 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions docs/store/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ export function getInitialState() {

## Reducer Factory

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

```ts
import { StoreModule, combineReducers, compose } from '@ngrx/store';
Expand All @@ -62,11 +62,11 @@ function debug(reducer) {
}
}

const debugReducerFactory = compose(debug, combineReducers);
const metaReducers = [debug];

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

```ts
// feature.module.ts
Expand Down
4 changes: 2 additions & 2 deletions example-app/app/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { CoreModule } from './core/core.module';
import { AuthModule } from './auth/auth.module';

import { routes } from './routes';
import { reducers } from './reducers';
import { reducers, metaReducers } from './reducers';
import { schema } from './db';

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

/**
* @ngrx/router-store keeps router state up-to-date in the store.
Expand Down
20 changes: 19 additions & 1 deletion example-app/app/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import {
ActionReducerMap,
createSelector,
createFeatureSelector,
compose,
ActionReducer,
combineReducers,
Action,
Expand Down Expand Up @@ -37,6 +36,25 @@ export const reducers: ActionReducerMap<State> = {
layout: fromLayout.reducer,
};

// console.log all actions
export function logger(reducer: ActionReducer<State>): ActionReducer<any, any> {
return function(state: State, action: any): State {
console.log('state', state);
console.log('action', action);

return reducer(state, action);
};
}

/**
* By default, @ngrx/store uses combineReducers with the reducer map to compose
* the root meta-reducer. To add more meta-reducers, provide an array of meta-reducers
* that will be composed to form the root meta-reducer.
*/
export const metaReducers: ActionReducer<any, any>[] = !environment.production
? [logger]
: [];

/**
* Layout Reducers
*/
Expand Down
4 changes: 3 additions & 1 deletion modules/store/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export {
} from './models';
export { StoreModule } from './store_module';
export { Store } from './store';
export { combineReducers, compose } from './utils';
export { combineReducers, compose, createReducerFactory } from './utils';
export { ActionsSubject, INIT } from './actions_subject';
export {
ReducerManager,
Expand All @@ -24,10 +24,12 @@ export {
export { State, StateObservable, reduceState } from './state';
export {
INITIAL_STATE,
_REDUCER_FACTORY,
REDUCER_FACTORY,
INITIAL_REDUCERS,
STORE_FEATURES,
_INITIAL_STATE,
META_REDUCERS,
} from './tokens';
export {
StoreRootModule,
Expand Down
1 change: 1 addition & 0 deletions modules/store/src/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface StoreFeature<T, V extends Action = Action> {
reducers: ActionReducerMap<T, V> | ActionReducer<T, V>;
reducerFactory: ActionReducerFactory<T, V>;
initialState?: InitialState<T>;
metaReducers?: ActionReducer<any, any>[];
}

export interface Selector<T, V> {
Expand Down
8 changes: 6 additions & 2 deletions modules/store/src/reducer_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
StoreFeature,
} from './models';
import { INITIAL_STATE, INITIAL_REDUCERS, REDUCER_FACTORY } from './tokens';
import { omit } from './utils';
import { omit, createReducerFactory } from './utils';
import { ActionsSubject } from './actions_subject';

export abstract class ReducerObservable extends Observable<
Expand All @@ -34,13 +34,17 @@ export class ReducerManager extends BehaviorSubject<ActionReducer<any, any>>
addFeature({
reducers,
reducerFactory,
metaReducers,
initialState,
key,
}: StoreFeature<any, any>) {
const reducer =
typeof reducers === 'function'
? reducers
: reducerFactory(reducers, initialState);
: createReducerFactory(reducerFactory, metaReducers)(
reducers,
initialState
);

this.addReducer(key, reducer);
}
Expand Down
17 changes: 15 additions & 2 deletions modules/store/src/store_module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,15 @@ import {
StoreFeature,
InitialState,
} from './models';
import { combineReducers } from './utils';
import { compose, combineReducers, createReducerFactory } from './utils';
import {
INITIAL_STATE,
INITIAL_REDUCERS,
REDUCER_FACTORY,
_REDUCER_FACTORY,
STORE_FEATURES,
_INITIAL_STATE,
META_REDUCERS,
} from './tokens';
import { ACTIONS_SUBJECT_PROVIDERS, ActionsSubject } from './actions_subject';
import {
Expand Down Expand Up @@ -62,6 +64,7 @@ export class StoreFeatureModule implements OnDestroy {
export type StoreConfig<T, V extends Action = Action> = {
initialState?: InitialState<T>;
reducerFactory?: ActionReducerFactory<T, V>;
metaReducers?: ActionReducer<T, V>[];
};

@NgModule({})
Expand Down Expand Up @@ -89,11 +92,20 @@ export class StoreModule {
? { provide: INITIAL_REDUCERS, useExisting: reducers }
: { provide: INITIAL_REDUCERS, useValue: reducers },
{
provide: REDUCER_FACTORY,
provide: META_REDUCERS,
useValue: config.metaReducers ? config.metaReducers : [],
},
{
provide: _REDUCER_FACTORY,
useValue: config.reducerFactory
? config.reducerFactory
: combineReducers,
},
{
provide: REDUCER_FACTORY,
deps: [_REDUCER_FACTORY, META_REDUCERS],
useFactory: createReducerFactory,
},
ACTIONS_SUBJECT_PROVIDERS,
REDUCER_MANAGER_PROVIDERS,
SCANNED_ACTIONS_SUBJECT_PROVIDERS,
Expand Down Expand Up @@ -130,6 +142,7 @@ export class StoreModule {
reducerFactory: config.reducerFactory
? config.reducerFactory
: combineReducers,
metaReducers: config.metaReducers ? config.metaReducers : [],
initialState: config.initialState,
},
},
Expand Down
4 changes: 4 additions & 0 deletions modules/store/src/tokens.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,9 @@ import { OpaqueToken } from '@angular/core';
export const _INITIAL_STATE = new OpaqueToken('_ngrx/store Initial State');
export const INITIAL_STATE = new OpaqueToken('@ngrx/store Initial State');
export const REDUCER_FACTORY = new OpaqueToken('@ngrx/store Reducer Factory');
export const _REDUCER_FACTORY = new OpaqueToken(
'@ngrx/store Reducer Factory Provider'
);
export const INITIAL_REDUCERS = new OpaqueToken('@ngrx/store Initial Reducers');
export const META_REDUCERS = new OpaqueToken('@ngrx/store Meta Reducers');
export const STORE_FEATURES = new OpaqueToken('@ngrx/store Store Features');
11 changes: 11 additions & 0 deletions modules/store/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,3 +83,14 @@ export function compose(...functions: any[]) {
return rest.reduceRight((composed, fn) => fn(composed), last(arg));
};
}

export function createReducerFactory(
reducerFactory: ActionReducerFactory<any, any>,
metaReducers?: ActionReducer<any, any>[]
): ActionReducerFactory<any, any> {
if (Array.isArray(metaReducers) && metaReducers.length > 0) {
return compose.apply(null, [...metaReducers, reducerFactory]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to apply:

return compose(...metaReducers, reducerFactory)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TypeScript doesn't agree with signature, which is why I used apply:

ERROR in ngrx/platform/modules/store/src/utils.ts (92,12): Supplied parameters do not match any signature of call target.

}

return reducerFactory;
}