# Migrate legacy NGRX

Before version 15 of NGRX, some old syntax and decorators were allowed but deprecated. I'll show you how to migrate a simple, but deprecated code to NGRX@15. The following example is mostly taken from a real-world application. There will be minor code refactoring included.

# Actions

The old way of creating actions.

* an enum `CountriesActionTypes` with action types
    
* each action as a class implementing `Action` interface
    
* union type `CountriesActions` with action class' references
    

```typescript
import { Action } from '@ngrx/store';

export enum CountriesActionTypes {
  LoadCountries = '[Countries] Load Countries',
  LoadCountriesSuccess = '[Countries] Load Countries Success',
  LoadCountriesFailure = '[Countries] Load Countries Failure',
}

export class LoadCountries implements Action {
  readonly type = CountriesActionTypes.LoadCountries;
}

export class LoadCountriesSuccess implements Action {
  readonly type = CountriesActionTypes.LoadCountriesSuccess;

  constructor(public payload: string[]) {
  }
}

export class LoadCountriesFailure implements Action {
  readonly type = CountriesActionTypes.LoadCountriesFailure;
}

export type CountriesActions = LoadCountries | LoadCountriesSuccess | LoadCountriesFailure;
```

To migrate:

* remove the enum with action types
    
* replace action classes with `createAction()` functions
    
    * use `props()` function to define action payload
        

```typescript
import { createAction, props } from '@ngrx/store';


export const requestCountries = createAction('[Countries] Request countries');
export const requestCountriesSuccess = createAction('[Countries] Request countries success', props<{ response: string[] }>());
export const requestCountriesError = createAction('[Countries] Request countries error', props<{ error: unknown }>());
```

# Reducer

The old reducer uses `switch` instruction to update state according to supported action type (taken from action's class `type` property).

```typescript
import { CountriesActions, CountriesActionTypes } from './countries.actions';


export const STATE_NAME = 'countries';

export interface CountriesState {
  loading?: boolean;
  error?: boolean;
  list?: string[];
}

export function countriesReducer(state: CountriesState = {}, action: CountriesActions): CountriesState {
  switch (action.type) {
    case CountriesActionTypes.LoadCountries:
      return {
        ...state,
        loading: true,
        error: false,
      };
    case CountriesActionTypes.LoadCountriesSuccess:
      return {
        ...state,
        list: action.payload,
        loading: false,
        error: false,
      };
    case CountriesActionTypes.LoadCountriesFailure:
      return {
        ...state,
        loading: false,
        error: true,
      };
    default:
      return state;
  }
}
```

To migrate:

* (minor) update state key names to be more verbose
    
* introduce `initialState`
    
* update `createReducer` function:
    
    * pass `initialState`
        
    * replace `switch` with `on()` functions (state change function)
        
        * the `on(actionCreator, reducer)` function takes 2 arguments
            
            * `actionCreator` - the reference to the created action
                
            * `reducer` - a function that takes the current state and current action and returns the updated state `(state, action) => ({...})`
                

```typescript
import { Action, createReducer, on } from '@ngrx/store';
import {
  requestCountries,
  requestCountriesError,
  requestCountriesSuccess,
} from './countries.actions';

export const STATE_NAME = 'countries';

export interface State {
  countriesResponse?: string[];
  countriesLoading: boolean;
  countriesError: unknown;
}

const initialState: State = {
  countriesResponse: undefined,
  countriesLoading: false,
  countriesError: undefined,
};

const reducer = createReducer(
  initialState,

  on(requestCountries, (state) => ({
    ...state,
    countriesResponse: undefined,
    countriesLoading: true,
    countriesError: undefined,
  })),
  on(requestCountriesError, (state, {error}) => ({
    ...state,
    countriesLoading: false,
    countriesError: error,
  })),
  on(requestCountriesSuccess, (state, action) => ({
    ...state,
    countriesResponse: action.response,
    countriesLoading: false,
  })),
);

export function countriesReducer(state: State | undefined, action: Action) {
  return reducer(state, action);
}
```

# Effects

Before version 7, you can find the usage of `ofType` function chained directly on `this.actions$` (injected `Actions`). This syntax was dropped in version 7 in favor of `ofType` operator.

```typescript
@Injectable()
export class CountriesEffects {
  @Effect()
  someEffect$: Observable<Action> = this.actions$
    .ofType(CountriesActionTypes.LoadCountries)
    .pipe(
      map((response) => new LoadCountriesSuccess(response)),
      catchError(() => of(new LoadCountriesFailure()))
    );

  constructor(private actions$: Actions) {}
}
```

The old effects use `@Effect` decorator which was replaced by `createEffect()` function.

```typescript
import { Injectable } from '@angular/core';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap } from 'rxjs/operators';
import { CountriesActionTypes, LoadCountriesFailure, LoadCountriesSuccess } from './countries.actions';
import { CountriesService } from './countries.service';

@Injectable()
export class CountriesEffects {
  @Effect()
  load: Observable<LoadCountriesSuccess | LoadCountriesFailure> = this.actions.pipe(
    ofType(CountriesActionTypes.LoadCountries),
    switchMap(() =>
      this.countriesService.list().pipe(
        map((response: string[]) => new LoadCountriesSuccess(response)),
        catchError(() => of(new LoadCountriesFailure()))
      )
    )
  );



  constructor(
    private actions: Actions,
    private countriesService: CountriesService
  ) {
  }
}
```

To migrate:

* use `createEffect()` function instead of `@Effect()` decorator
    
* use `ofType()` operator to filter the desired action
    
* (minor) rename the effect property to be more verbose and end it with `$` which indicates that it's an observable
    

```typescript
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, mergeMap, switchMap } from 'rxjs/operators';
import { of } from 'rxjs';
import {
  requestCountries,
  requestCountriesError,
  requestCountriesSuccess,
} from './countries.actions';
import { CountriesService } from './countries.service';

@Injectable()
export class CountriesEffects {
  requestCountries$ = createEffect(() =>
    this.actions$.pipe(
      ofType(requestCountries),
      mergeMap((action) =>
        this.countriesService.getCountries().pipe(
          switchMap((response) => of(requestCountriesSuccess({response}))),
          catchError((error) => of(requestCountriesError({error})))
        )
      )
    )
  );

  constructor(
    private readonly actions$: Actions,
    private readonly countriesService: CountriesService,
  ) {
  }
}
```

# Selectors

If you were using **selectors with props** they might also need migration. You have to replace them with **factory selectors**.

```typescript
const selectFirstNCountries = createSelector(
    selectCountries,
    (countries, props: { count: number }) => {
      return countries?.slice(count);
    }
);
```

To migrate:

* create a factory with a parameter instead of a selector with props
    

```typescript
const selectFirstNCountries = (count: number) =>
  createSelector(
    selectCountries,
    (countries) => {
      return countries?.slice(count);
    }
  );
```
