Redux/NGRX is awesome, it gives us predictable immutable state containers that enable efficient change detection and de-coupling of components. All of this comes at a cost, we have to write verbose code that ends up being quite a bit of boilerplate. A typical implementation for a single store includes:

Reducer

Actions

Type Constants

Selectors

Effects

Thats a lot of stuff!

As I build out project after project using NGRX, I kept wondering how can I reduce this. My first thought was to write my own store where I would master the boilerplate but the more I toiled on that the more I went in circles and eventually ended back up at the same spot.

As I research I found there is a number of Redux libraries out there that redux-act and redux-actions out there that try to help do this for plain Redux. The official documentation even has a whole section on reducing boilerplate.

After going round and round, I sought out to try to merge some of the concepts of the Redux libraries with NGRX and see where I can end up. In the end, I created ngrx-actions which is a library that provides utilities around NGRX to reduce boilerplate. ngrx-actions plugs into your existing app and drops in without having to change the entire app. Under the hood is quite simple and returns the same function that NGRX is already expecting so things like tests continue to work even!

Before we dive into ngrx-actions, lets look at where we are at today…

Today’s boilerplate

First lets look at the typical boilerplate we have today.

Reducer

Actions

Effects

After spending countless hours on this same code repeated over and over again, I started to realize a few things a few common trends…

We pass string constants ( or enums ) all over the place to identify the type of action. We use these constants in our action definitions, reducer, effects and when dispatching events.

this.store.dispatch({ type: ORDER_PIZZA, payload: toppings });

Typically in NGRX, you define a action class and have the store dispatch an instance of that class like:

this.store.dispatch(new OrderPizza(toppings));

This helps us add strong typing that is FSA compliant and does help reduce some string constant references. We still need the constants in the reducer and effects to determine when to pick that action. The more I studied this the more I realized since we already have a class instance that contains the types, why not just read it off the class itself in the reducer and effects? That would reduce having to pass around the string constant!

Since Angular uses decorators and classes extensively, I wanted to adopt those patterns (and personally switch statements drive me nuts but Dan has some good points here on why that is the way it is ). For the reducers I created a decorator that accepts the action class like:

export class PizzaStore {

@Action(OrderPizza)

orderPizza(state, action) { return { ...state, cooking: true }; }

}

All this decorator does is tell the pizza store that there is a method that is listening for the action OrderPizza .

Next, we need to turn this class into something that NGRX can understand. We know that NGRX expects a function with a state and action as arguments. We can easily create a factory pattern that will do matching for us ( don’t worry all this is under the hood ). That looks something like this:

export const createReducer = (klass) =>

(state, action) => klass[action.type](state, action);

now all we need to do is pass our class to our factory pattern like:

export const pizzaReducer = createReducer(PizzaStore);

and we have a NGRX compatible reducer!

Since we are no longer using a switch statement we can also remove the action type export const PizzaActionTypes = Action | Action | Action code too.

Now that we’ve eliminated the need for passing the string constant in the reducer, lets do the same thing for the effects. Recently rxjs introduced lettable operators, which are AWESOME! Now that this is available, all we need to do is create a lettable operator that will do something similar to what we did for the reducer.

@Effect() this.updates$.pipe(ofAction(OrderPizza)).subscribe(() => {...})

And just like that we have got rid of the need for passing string constants everywhere!

Another thing that is all over your code is: spreads! We need them to create new state objects when we manipulate our state object. But if you are always gonna spread why not let our reducer factory handle this for us?

If we made our factory handle this, we could change our action code to be much simpler:

export class PizzaStore {

@Action(OrderPizza)

orderPizza(state, action) { state.cooking = true; }

}

In our factory, we can read if the action didn’t return anything and automatically do the spread for us:

export const createReducer = (klass) => (state, action) => {

let res = klass[action.type](state, action);

if (!res) res = Array.isArray(state) ? [...state] : {...state};

return res;

}

If we don’t want to mutate the state, for example the action didn’t actually change anything, we can go ahead and just return the state and it will not create a new instance! Presto, goodbye spreads!

Coming all together…

Lets take the example NGRX reducer/effect and see what it would look like with ngrx-actions…

export class CookPizza { readonly type = 'Cook pizza' }

export class OrderPizza {

readonly type = 'Order Pizza';

constructor(public payload: string[]) {}

} @Store({ toppings: [], cooking: false })

export class PizzaStore {

@Action(CookPizza) cookPizza(state) { state.cooking = true } @Action(OrderPizza)

orderPizza(state, { payload }) { state.toppings = payload }

} @Injectable()

export class PizzaEffects {

constructor(

private store: Store<any>,

private update$: Actions,

private pizzaService: PizzaService) {} @Effect() orderPizza$ = this.update$.pipe(

ofAction(OrderPizza),

switchMap(toppings => this.pizzaService.order(toppings)),

map(res => new OrderPizzaSuccess(res)));

} export const pizzaReducer = createReducer(PizzaStore);

Not bad huh? ngrx-actions is still new and evolving but I’ve had great success converting a very large code base to use it recently in a short amount of time and the result is very clean. I hope it can help you too!

I’m #ngPanda

I hope you enjoyed the post, if you liked it follow me on Twitter and Github for more JavaScript tips/opinions/projects/articles/etc!