Reusing ngrx reducers using higher-order functions
Tags: angular, ngrx, higherorder, reducers
Published: 2018-07-22 18:05:07

---
title: Reusing ngrx reducers using higher-order functions
published: true
description: Two examples of how to use higher-order functions to reuse reducers.
tags: angular, ngrx, higher-order, reducers
---

Reducers are pure functions that specify how the application's state changes in response to actions sent to the store.

Usually, there's a one to one relationship between a store key and a reducer, but **what happens when you want to use the same reducer logic in more than one place** because, for example, you need various instances of the reducer output?


# Higher-order functions

So, what are higher-order functions (HoF) anyway?

> Functions that operate on other functions, either by taking them as arguments or by returning them, are called higher-order functions. <small>([source](https://eloquentjavascript.net/05_higher_order.html#p_cao2fH68Tj))</small>

There's nothing remarkable about that, but this statement is what makes such functions special:

> Higher-order functions allow us to abstract over actions, not just values. <small>([source](https://eloquentjavascript.net/05_higher_order.html#p_+cgNTV2i2y))</small>

Higher-order functions allow you to declare what stuff is instead of defining steps that change some state.
While a composition of higher-order functions carries semantic overload, a sequential manipulation of values carries a cognitive overload. You pick one.


# HoF and reducers

A higher-order reducer is a function that returns a (configured) reducer.

Higher order reducers allow us to parametrize which actions are accepted by a reducer.
There are several strategies to achieve that. We'll take a look at a couple of them and see each one's pros and cons.

First of all, I encourage you to have a look at the basic (without higher-order reducers) canonical example that we're going to be using: the counter application.  ([stackblitz link](https://stackblitz.com/edit/angular-vhvefd?file=src/app/app.module.ts))


### Dynamically creating action names

Our first higher-order reducer will use a straightforward technique. We will configure the reducer by dynamically defining which action types accept.( [stackblitz link](https://stackblitz.com/edit/angular-udcj8y?file=src/app/app.module.ts) )

Let's see how the modified `counter.component.ts` looks like:


```typescript
@Component({
  selector: 'counter',
  template: `
    <button (click)="increment()">Increment</button>
    <div>Current Count: {{ count$ | async }}</div>
    <button (click)="decrement()">Decrement</button>

    <button (click)="reset()">Reset Counter</button>
  `,
})
export class CounterComponent implements OnInit {
  @Input() counterName: string;
  count$: Observable<number>;

  constructor(private store: Store<AppState>) {}

  ngOnInit() {
    this.count$ = this.store.pipe(select(this.counterName));
  }

  increment() {
    this.store.dispatch({ type: `${this.counterName}.${INCREMENT}` });
  }

  decrement() {
    this.store.dispatch({ type: `${this.counterName}.${DECREMENT}` });
  }

  reset() {
    this.store.dispatch({ type: `${this.counterName}.${RESET}` });
  }
}
```

The critical change here is the fact that our component now accepts a `counterName` input, which will define the name of the store key that this component will be using as the data source and also what prefix the action `type` will have.

And this is how we use the counter component in the `app.component.html` template: 


```
<counter [counterName]="'counter1'"></counter>
<hr>
<counter [counterName]="'counter2'"></counter>
```

Note that we have added two instances of the component. Each will be using its own dynamically created reducer.

Lets' see how the higher-order reducer looks like:


`counter.reducer.ts`
```typescript
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const RESET = 'RESET';


export function counterReducer(counterName: string) {
  const initialState = 0;

  return function reducer(state: number = initialState, action: Action) {
    switch (action.type) {
      case `${counterName}.${INCREMENT}`:
        return state + 1;

      case `${counterName}.${DECREMENT}`:
        return state - 1;

      case `${counterName}.${RESET}`:
        return 0;

      default:
        return state;
    }
  }
}
```

The higher-order reducer differs from the original implementation in three aspects:
- It returns the reducer function, not the new state.
- It wraps its state in the closure scope.
- The matched reducer action types are defined by prefixing the original action `type` with the parametrized `counterName` (this is flexible, others prefer suffixes, you have to be consistent).
There's not much more to say.


Last but not least, we need to change the `Store` configuration. We need to add the store keys and use the higher-order reducer to assign their value.


`app.module.ts`
```typescript
@NgModule({
  imports: [
    BrowserModule,
    StoreModule.forRoot({ 
      counter1: counterReducer('counter1'),
      counter2: counterReducer('counter2')
    })
  ],
  declarations: [AppComponent, CounterComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }
```

And that's pretty much it.
The `counterReducer('...')` function returns a new  reducer each time it's called, and this reducer accepts action types in the form `[actionName].[action.type]`.


### Filtering actions by its payload

Very quickly, now that we understand the mechanism let's see another more type-safe strategy to implement higher-order reducers.
The philosophy is the same, but we'll be using action creators instead of dynamic action type names, and we'll also be adding more types to the table. ([stackblitz link](https://stackblitz.com/edit/angular-uyr8sx?file=src/app/app.module.ts))

The differences between this project and the more dynamic one reside in just two files:


`counter.reducer.ts`
```typescript
export const INCREMENT = 'INCREMENT';
export const DECREMENT = 'DECREMENT';
export const RESET = 'RESET';


// Action interface for higher-order reducers

interface CounterAction extends Action {
  actionPrefix: string;
}


// Action creators

export class Increment implements CounterAction {
  type = INCREMENT;
  constructor(public actionPrefix: string) { }
}

export class Decrement implements CounterAction {
  type = DECREMENT;
  constructor(public actionPrefix: string) { }
}

export class Reset implements CounterAction {
  type = RESET;
  constructor(public actionPrefix: string) { }
}

export type CounterActions =
  | Increment
  | Decrement
  | Reset;


// Reducer

export function counterReducer(actionPrefix: string) {
  const initialState = 0;

  function reducer(state: number = initialState, action: CounterActions) {
    switch (action.type) {
      case INCREMENT:
        return state + 1;

      case DECREMENT:
        return state - 1;

      case RESET:
        return 0;

      default:
        return state;
    }
  }

  return (state: number = initialState, action: CounterActions) => {
    switch (action.actionPrefix) {
      case actionPrefix:
        return reducer(state, action);
      default:
        return state;
    }
  }
}
```

A lot has changed here. Fortunately, there aren't many changes in the other file.


`counter.component.ts`
```typescript
@Component({
  selector: 'counter',
  template: `
    <button (click)="increment()">Increment</button>
    <div>Current Count: {{ count$ | async }}</div>
    <button (click)="decrement()">Decrement</button>

    <button (click)="reset()">Reset Counter</button>
  `,
})
export class CounterComponent implements OnInit {
  @Input() counterName: string;
  count$: Observable<number>;

  constructor(private store: Store<AppState>) {}

  ngOnInit() {
    this.count$ = this.store.pipe(select(this.counterName));
  }

  increment() {
    this.store.dispatch(new Increment(this.counterName));
  }

  decrement() {
    this.store.dispatch(new Decrement(this.counterName));
  }

  reset() {
    this.store.dispatch(new Reset(this.counterName));
  }
}
```

The main difference is that we have introduced the action creators, which satisfy the newly added `CounterAction` interface and makes the `actionPrefix` property mandatory for all its implementors.
Also, the `CounterActions` type allows us to be much more specific about which actions the reducer accepts.


# Pros and cons

### Dynamically creating action names
- Pros
    - Very straightforward.

- Cons
    - It lacks type safety.

### Filtering actions by its payload
- Pros
    - Typesafe.

- Cons
    - More verbose.


# Some links

- Read the [Eloquent Javascript book chapter](https://eloquentjavascript.net/05_higher_order.html) about higher-order functions
- The Redux documentation has [an excellent description](https://redux.js.org/recipes/structuring-reducers/reusing-reducer-logic#customizing-behavior-with-higher-order-reducers) of what a higher reducer is and does.