DEV Community

Naimul Karim
Naimul Karim

Posted on

Refactoring a component from @Input()/@Output() to NgRx?

1. Original Angular component with @Input and @Output

Let’s say you have a Counter Component:

// counter.component.ts

import { Component, Input, Output, EventEmitter } from '@angular/core';
@Component({
selector: 'app-counter',
template: `
<div>
<button (click)="decrement()">-</button>
<span>{{ count }}</span>
<button (click)="increment()">+</button>
</div>
`,
})
export class CounterComponent {
@Input() count = 0;
@Output() countChange = new EventEmitter<number>();
increment() {
this.countChange.emit(this.count + 1);
}
decrement() {
this.countChange.emit(this.count - 1);
}
}
Enter fullscreen mode Exit fullscreen mode

2. Now let’s convert it to use NgRx for state

First, assume you already have an NgRx store set up.

We’ll need:

Actions
Reducer
Selectors
Dispatch and Select in the component

Step 1: Create Actions

// counter.actions.ts
import { createAction } from '@ngrx/store';

export const increment = createAction('[Counter] Increment');
export const decrement = createAction('[Counter] Decrement');
Enter fullscreen mode Exit fullscreen mode

Step 2: Create Reducer

// counter.reducer.ts
import { createReducer, on } from '@ngrx/store';
import * as CounterActions from './counter.actions';

export interface CounterState {
  count: number;
}

export const initialState: CounterState = {
  count: 0,
};

export const counterReducer = createReducer(
  initialState,
  on(CounterActions.increment, (state) => ({ ...state, count: state.count + 1 })),
  on(CounterActions.decrement, (state) => ({ ...state, count: state.count - 1 }))
);
Enter fullscreen mode Exit fullscreen mode

Step 3: Create Selectors

// counter.selectors.ts

import { createSelector, createFeatureSelector } from '@ngrx/store';
import { CounterState } from './counter.reducer';

export const selectCounterState = createFeatureSelector<CounterState>('counter');

export const selectCount = createSelector(
  selectCounterState,
  (state) => state.count
);
Enter fullscreen mode Exit fullscreen mode

Step 4: Update the Component

// counter.component.ts
import { Component } from '@angular/core';
import { Store } from '@ngrx/store';
import { Observable } from 'rxjs';
import * as CounterActions from './counter.actions';
import { selectCount } from './counter.selectors';

@Component({
  selector: 'app-counter',
  template: `
    <div>
      <button (click)="decrement()">-</button>
      <span>{{ count$ | async }}</span>
      <button (click)="increment()">+</button>
    </div>
  `,
})
export class CounterComponent {
  count$: Observable<number>;

  constructor(private store: Store) {
    this.count$ = this.store.select(selectCount);
  }

  increment() {
    this.store.dispatch(CounterActions.increment());
  }

  decrement() {
    this.store.dispatch(CounterActions.decrement());
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)