Closed laurentgoudet closed 7 years ago
Injection tokens for meta reducers already exist in the API. If you need lower level access to managing reducers, you should use the ReducerManager
, as its what's used underneath to add/remove/replace reducers.
Thanks @brandonroberts, I may have missed something but StoreModule.forRoot()
only takes a StoreConfig
object
export class StoreModule {
static forRoot<T, V extends Action = Action>(
reducers: ActionReducerMap<T, V> | InjectionToken<ActionReducerMap<T, V>>,
config?: StoreConfig<T, V>
): ModuleWithProviders;
whose type is
export type StoreConfig<T, V extends Action = Action> = {
initialState?: InitialState<T>;
reducerFactory?: ActionReducerFactory<T, V>;
metaReducers?: MetaReducer<T, V>[];
};
Obviously TypeScript then complains that InjectionToken<MetaReducer<T, V>>
isn't the same thing as MetaReducer<T, V>
, although it won't work anyway since only INITIAL_REDUCERS
and FEATURE_REDUCERS
use injector.get()
.
And the ReducerManager
addFeature
& addReducer
APIs would scope the reducer to a specific key / feature, so can't be used for meta reducers:
addReducer(key: string, reducer: ActionReducer<any, any>) {
this.reducers = { ...this.reducers, [key]: reducer };
this.updateReducers();
}
What's the proper way to achieve that? I've managed to make it work by manually composing my reducers using compose
, it only feels weird that my code is taking care of reducer composition but I guess that's fine.
export function getReducers(errorHandler: ErrorHandler) {
return compose.apply(null, [
tryCatchReducerFactory(errorHandler),
])(reducers);
}
What's the proper way to achieve that? I've managed to make it work by manually composing my reducers using compose, it only feels weird that my code is taking care of reducer composition but I guess that's fine.
Actually that doesn't work since when using an InjectionToken
StoreModule.forRoot
still expect an reducers map, and not an already compose
d root reducer, so manual reducer composition can't do the trick. Am I missing something?
@laurentgoudet I was referring to the META_REDUCERS
token for register meta reducers in the root.
import { META_REDUCERS } '@ngrx/store';
import { SomeService } from './some.service';
export function getMetaReducers(some: SomeService): MetaReducer[] {
// return array of meta reducers;
}
@NgModule({
providers: [
{
provide: META_REDUCERS,
deps: [SomeService],
useFactory: getMetaReducers
}
]
})
export class AppModule {}
Thanks for getting back to me, after re-reading it this was exactly what you said in your initial reply, and it indeed works perfectly. I need more coffee :).
😄
@brandonroberts I'm sorry to reopen the conversation, but am I able to have the same behaviour on features modules? My metareducer is related to the feature I'm developing, so it's totally useless to me to have its registration in root module.
TIA
I agree with @samueledirito. I need the ability to inject services into MetaReducers that are specified using
Thanks!
As per the documentation, root reducers can be injected using an
InjectionToken
and aProvider
to register the reducers through dependency injection.Being able to do the same thing for meta-reducers is useful, as those often have side-effects like sending errors to a monitoring service, synchronizing state with
sessionStorage
.., which would benefit from Angular's DI framework.This was apparently possible before v4 (https://github.com/ngrx/store/issues/313), but I couldn't find a way to do it now that the store handles the reducers composition. Is there a way to do that? Or could
StoreConfig
accept anInjectionToken<ActionReducer<T, V>>[]
similarly to theforRoot
'sreducers
param and inject the meta-reducers?