Solving AOT Error in NgRx: Function calls are not supported in decorators

Siyang Kern Zhao
Angular In Depth
Published in
3 min readSep 3, 2019
Photo by chuttersnap on Unsplash

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

Problem

According to NgRx quick start guide, the following is code snippet of how we set up NgRx.

export const counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
@NgModule({
imports: [
StoreModule.forRoot({ count: counterReducer })
],
...
})
export class AppModule { }

When you run ng serve, there is no problem.

However when you build the Angular project using prod mode (AOT by default) ng build --prod, you will run into an error like:

ERROR in src/app/app.module.ts(33,25): Error during template compile of 'AppModule'
Function calls are not supported in decorators but 'createAction' was called in 'reducers'

The core explanation for this error is well-covered in the Angular docs.

Solution 1: Wrap with a new function

const _counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
export function counterReducer(state, action) {
return _counterReducer(state, action)
}
@NgModule({
imports: [
StoreModule.forRoot({ count: counterReducer })
],
...
})
export class AppModule { }

This works well. And NgRx is updating its docs to add this wrapper function.

Caveat

This adds the boilerplate of writing a wrapping function for each sub-reducer.

Solution 2: Use InjectionToken

As documented in NgRx, StoreModule.forRoot can also takes in a InjectionToken which refers to provided root reducer map. This is a perfect work around for the AOT error.

export const counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
export const ROOT_REDUCER = new InjectionToken<any>('Root Reducer');@NgModule({
imports: [
StoreModule.forRoot(ROOT_REDUCER)
],
providers: [
{provide: ROOT_REDUCER, useValue: { count: counterReducer }},
],
})
export class AppModule { }

Please note that the above code is just for illustration purpose, it is recommended to use proper file structure and proper typings.

Additional Notes

  1. If you use Ivy, you won’t need to worry about this issue as no AOT errors like this will be thrown.
  2. For solution 2, you can also embed reducer value while creating injectionToken. (Thanks to Serkan Sipahi for bring up this point)
export const counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
export const ROOT_REDUCER = new InjectionToken<any>('Root Reducer', {factory: () => ({count: counterReducer})});@NgModule({
imports: [
StoreModule.forRoot(ROOT_REDUCER)
],
providers: [
// remove provider here
],
})
export class AppModule { }

Credits

Thanks for peer reviewer Serkan Sipahi and Emily Xiong whose article gave me inspiration.

--

--