제3부분.Pokédex 구축:create* 함수를 사용하여 NgRX 개선
76376 단어 angulartypescriptbeginnersjavascript
소개
본고에서 우리는 Angular 프레임워크와 NgRX 주 관리 라이브러리로 Pokédex를 개발할 것이다.NgRX 8에서 새로 만든 * 기능을 사용할 예정입니다.
이 글을 정확하게 이해하기 위해서는 중간 단계에서 어떻게 Angular를 관리하는지, 그리고 상태 관리 라이브러리가 무엇인지 아는 것이 가장 좋다.이 시리즈에서 우리는 당신의 NgRX 학습을 보충할 수 있는 특정한 예시 (포켓몬) 를 개발하는 방법을 보여 드리겠습니다.
먼저 아래의 GIF는 이러한 기둥을 따라 구축된 결과를 보여 줍니다.
본 시리즈의 first과second 부분을 읽고 구축 중인 내용을 이해하는 것이 중요하다.본고에서 우리는 @ngrx/entity
패키지의create* 함수를 사용하여 이전에 이 시리즈에서 개발한 코드를 개선할 것이다. 이것은 동작, 감축기와 효과를 만드는 데 필요한 샘플 코드를 간소화할 것이다.
작업 작성
NgRX에서 작업을 작성하려면 많은 템플릿 코드가 필요합니다.매거진, 동작 유형, 클래스, 연합 유형을 만들어야 합니다.이 새 버전에서는 조작을 더욱 쉽게 만들 수 있습니다.
NgRX 핵심 팀은 유명한 공장 기능 설계 모델을 사용하여 이 목표를 실현했다.공장 함수는createAction입니다.createAction 함수는 두 개의 매개 변수를 수신합니다.
NgRX에서 작업을 작성하려면 많은 템플릿 코드가 필요합니다.매거진, 동작 유형, 클래스, 연합 유형을 만들어야 합니다.이 새 버전에서는 조작을 더욱 쉽게 만들 수 있습니다.
NgRX 핵심 팀은 유명한 공장 기능 설계 모델을 사용하여 이 목표를 실현했다.공장 함수는createAction입니다.createAction 함수는 두 개의 매개 변수를 수신합니다.
동작 유형.동작을 식별하는 데 사용되는 유명한 문자열입니다.
도구.운영 메타데이터입니다.예를 들어, 유효 하중.
이전
export class LoadPokemon implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS;
constructor() {}
}
export class LoadPokemonSuccess implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_SUCCESS;
constructor(public payload: Array<Pokemon>) {}
}
이후loadPokemonFailed = createAction(
PokemonActionTypes.LOAD_POKEMONS_FAILED,
props<{ message: string }>()
),
add: createAction(PokemonActionTypes.ADD, props<{ pokemon: Pokemon }>()),
before 코드에서 Action
인터페이스를 실현하는 클래스를 만들고 구조 함수 정의 type
속성과 payload
를 사용해야 합니다.다른 한편, 후면 코드에서는 createAction
함수를 사용하여 작업을 만들 수 있습니다. 첫 번째 인자는 type
, 두 번째 인자는 props
속성입니다. (우리의 상하문에서 유효 부하가 될 것입니다.)비록 핵심 팀은 매거를 사용할 필요가 없다고 성명하지만, 나의 특정한 인코딩 스타일에서 나는 동작 매거를 정의하여 동작 집합을 이해하는 것을 더욱 좋아한다.
따라서 전후
pokemon.action.ts
:import { Action } from '@ngrx/store';
import { Pokemon } from '@models/pokemon.interface';
export enum PokemonActionTypes {
ADD = '[Pokemon] Add',
ADD_SUCCESS = '[Pokemon] Add success',
ADD_FAILED = '[Pokemon] Add failed',
LOAD_POKEMONS = '[Pokemon] Load pokemon',
LOAD_POKEMONS_SUCCESS = '[Pokemon] Load pokemon success',
LOAD_POKEMONS_FAILED = '[Pokemon] Load pokemon failed',
UPDATE = '[Pokemon] Update',
UPDATE_SUCCESS = '[Pokemon] Update success',
UPDATE_FAILED = '[Pokemon] Update failed',
DELETE = '[Pokemon] Delete',
DELETE_SUCCESS = '[Pokemon] Delete success',
DELETE_FAILED = '[Pokemon] Delete failed'
}
export class LoadPokemon implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS;
constructor() {}
}
export class LoadPokemonSuccess implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_SUCCESS;
constructor(public payload: Array<Pokemon>) {}
}
export class LoadPokemonFailed implements Action {
readonly type = PokemonActionTypes.LOAD_POKEMONS_FAILED;
constructor(public message: string) {}
}
export class Add implements Action {
readonly type = PokemonActionTypes.ADD;
constructor(public pokemon: Pokemon) {}
}
export class AddSuccess implements Action {
readonly type = PokemonActionTypes.ADD_SUCCESS;
constructor(public pokemon: Pokemon) {}
}
export class AddFailed implements Action {
readonly type = PokemonActionTypes.ADD_FAILED;
constructor(public message: string) {}
}
export class Delete implements Action {
readonly type = PokemonActionTypes.DELETE;
constructor(public id: number) {}
}
export class DeleteSuccess implements Action {
readonly type = PokemonActionTypes.DELETE_SUCCESS;
constructor(public id: number) {}
}
export class DeleteFailed implements Action {
readonly type = PokemonActionTypes.DELETE_FAILED;
constructor(public message: string) {}
}
export class Update implements Action {
readonly type = PokemonActionTypes.UPDATE;
constructor(public pokemon: Pokemon) {}
}
export class UpdateSuccess implements Action {
readonly type = PokemonActionTypes.UPDATE_SUCCESS;
constructor(public pokemon: Pokemon) {}
}
export class UpdateFailed implements Action {
readonly type = PokemonActionTypes.UPDATE_FAILED;
constructor(public message: string) {}
}
export type PokemonActions =
| LoadPokemonSuccess
| Add
| AddSuccess
| AddFailed
| Delete
| DeleteSuccess
| DeleteFailed
| Update
| UpdateSuccess
| UpdateFailed;
import { createAction, props } from '@ngrx/store';
import { Pokemon } from '@models/pokemon.interface';
export enum PokemonActionTypes {
ADD = '[Pokemon] Add',
ADD_SUCCESS = '[Pokemon] Add success',
ADD_FAILED = '[Pokemon] Add failed',
LOAD_POKEMONS = '[Pokemon] Load pokemon',
LOAD_POKEMONS_SUCCESS = '[Pokemon] Load pokemon success',
LOAD_POKEMONS_FAILED = '[Pokemon] Load pokemon failed',
UPDATE = '[Pokemon] Update',
UPDATE_SUCCESS = '[Pokemon] Update success',
UPDATE_FAILED = '[Pokemon] Update failed',
REMOVE = '[Pokemon] Delete',
REMOVE_SUCCESS = '[Pokemon] Delete success',
REMOVE_FAILED = '[Pokemon] Delete failed'
}
export const actions = {
loadPokemon: createAction(PokemonActionTypes.LOAD_POKEMONS),
loadPokemonSuccess: createAction(
PokemonActionTypes.LOAD_POKEMONS_SUCCESS,
props<{ pokemons: Pokemon[] }>()
),
loadPokemonFailed: createAction(
PokemonActionTypes.LOAD_POKEMONS_FAILED,
props<{ message: string }>()
),
add: createAction(PokemonActionTypes.ADD, props<{ pokemon: Pokemon }>()),
addSuccess: createAction(
PokemonActionTypes.ADD_SUCCESS,
props<{ pokemon: Pokemon }>()
),
addFailed: createAction(
PokemonActionTypes.ADD_FAILED,
props<{ message: string }>()
),
remove: createAction(PokemonActionTypes.REMOVE, props<{ id: number }>()),
removeSuccess: createAction(
PokemonActionTypes.REMOVE_SUCCESS,
props<{ id: number }>()
),
removeFailed: createAction(
PokemonActionTypes.REMOVE_FAILED,
props<{ message: string }>()
),
update: createAction(
PokemonActionTypes.UPDATE,
props<{ pokemon: Pokemon }>()
),
updateSuccess: createAction(
PokemonActionTypes.UPDATE_SUCCESS,
props<{ pokemon: Pokemon }>()
),
updateFailed: createAction(
PokemonActionTypes.UPDATE_FAILED,
props<{ message: string }>()
)
};
action
const를 내보냈습니다. 이것은 사전입니다. 동작 이름은 키로 하고 동작 자체는 값으로 합니다.createAction은 ActionCreator라는 함수를 되돌려주는 공장 함수입니다. 이 함수는 호출할 때 동작 대상을 되돌려줍니다.따라서 작업을 할당하려면 ActionCreator를 호출해야 합니다.
this.store.dispatch(addSuccess(pokemon: Pokemon));
동작 클래스와 연결된 대상을 만들 필요가 없습니다. 이 함수를 직접 호출할 수 있습니다.따라서 동작을 만드는 모든 효과에 다음 재구성을 적용해야 합니다.
이전
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => new PokemonActions.LoadPokemonSuccess(pokemons)),
catchError(message => of(new PokemonActions.LoadPokemonFailed(message)))
)
)
);
이후@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
다음 절에서createEffects 함수를 사용하여 효과 자체를 재구성합니다.createEffects
NgRx8은 createEffect
방법을 제공했는데 이 방법은 @Effect()
장식기의 대체 방법이다.데코레이터가 아닌 사용createEffect
의 주요 장점은 유형이 안전하다는 것이다.효과가 돌아오지 않으면 Observable<Action>
컴파일 오류가 발생한다는 것이다.
다음 코드 세션에서 새로운 loadAllPokemon$
방법의 전후 createEffect
효과를 보여 드리겠습니다.이전이 매우 쉽다.
이전
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
이후
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
따라서 전후pokemon.effects.ts
:
이전
import * as PokemonActions from '@states/pokemon/pokemon.actions';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { PokemonService } from '@services/pokemon.service';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.PokemonActionTypes.ADD_SUCCESS,
PokemonActions.PokemonActionTypes.UPDATE_SUCCESS,
PokemonActions.PokemonActionTypes.DELETE_SUCCESS,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_SUCCESS
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.PokemonActionTypes.ADD_FAILED,
PokemonActions.PokemonActionTypes.UPDATE_FAILED,
PokemonActions.PokemonActionTypes.DELETE_FAILED,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_FAILED
];
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(response => new PokemonActions.LoadPokemonSuccess(response)),
catchError(error => of(new PokemonActions.LoadPokemonFailed(error)))
)
)
);
@Effect()
addPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.ADD),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => new PokemonActions.AddSuccess(pokemon)),
catchError(error => of(new PokemonActions.AddFailed(error)))
)
)
);
@Effect()
deletePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.DELETE),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => new PokemonActions.DeleteSuccess(id)),
catchError(error => of(new PokemonActions.DeleteFailed(error)))
)
)
);
@Effect()
updatePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.UPDATE),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => new PokemonActions.UpdateSuccess(pokemon)),
catchError(error => of(new PokemonActions.UpdateFailed(error)))
)
)
);
@Effect({ dispatch: false })
successNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
);
@Effect({ dispatch: false })
failedNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
);
}
이후
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { actions as PokemonActions } from '@states/pokemon/pokemon.actions';
import { PokemonService } from '@services/pokemon.service';
import { of } from 'rxjs';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.addSuccess,
PokemonActions.updateSuccess,
PokemonActions.removeSuccess,
PokemonActions.loadPokemonSuccess
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.addFailed,
PokemonActions.updateFailed,
PokemonActions.removeFailed,
PokemonActions.loadPokemonFailed
];
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
addPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.add),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => PokemonActions.addSuccess({ pokemon })),
catchError(message => of(PokemonActions.addFailed({ message })))
)
)
)
);
deletePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.remove),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => PokemonActions.removeSuccess({ id })),
catchError(message => of(PokemonActions.removeFailed({ message })))
)
)
)
);
updatePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.update),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => PokemonActions.updateSuccess({ pokemon })),
catchError(message => of(PokemonActions.updateFailed(message)))
)
)
)
);
successNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
),
{ dispatch: false }
);
failedNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
),
{ dispatch: false }
);
}
이전에 모든 효과에 전달된 dispatch: false
매개 변수는 현재 createEffect
방법에 전달된 두 번째 매개 변수입니다.옵션 { dispatch: false }
은 새로운 동작을 보내지 않는 효과에 사용되며, 이 옵션을 추가하면 Observable<Action>
을 되돌려야 하는 효과 제한을 없앨 수 있습니다.
이경관
새로운 createReducer
방법은 switch
문장을 사용하지 않는 상황에서 감속기를 만들 수 있습니다.동작 유형을 구분하고 새로운 상태 참조를 되돌려주는 새로운 on
방법이 있습니다.또 다른 흥미로운 사실은 감속기에서 처리되지 않은 조작을 위해 기본 상황을 처리할 필요가 없다는 것이다.
따라서 전후pokemon.reducers.ts
:
이전
import { PokemonActionTypes, PokemonActions } from './pokemon.actions';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
export function pokemonReducer(
state: PokemonState = pokemonInitialState(),
action: PokemonActions
): PokemonState {
switch (action.type) {
case PokemonActionTypes.LOAD_POKEMONS_SUCCESS:
return pokemonAdapter.addAll(action.payload, state);
case PokemonActionTypes.ADD_SUCCESS:
return pokemonAdapter.addOne(action.pokemon, state);
case PokemonActionTypes.DELETE_SUCCESS:
return pokemonAdapter.removeOne(action.id, state);
case PokemonActionTypes.UPDATE_SUCCESS:
const { id } = action.pokemon;
return pokemonAdapter.updateOne(
{
id,
changes: action.pokemon
},
state
);
default:
return state;
}
}
이후
import { Action, createReducer, on } from '@ngrx/store';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
import { actions as PokemonActions } from './pokemon.actions';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
const pokemonReducer = createReducer(
pokemonInitialState(),
on(PokemonActions.loadPokemonSuccess, (state, { pokemons }) =>
pokemonAdapter.addAll(pokemons, state)
),
on(PokemonActions.addSuccess, (state, { pokemon }) =>
pokemonAdapter.addOne(pokemon, state)
),
on(PokemonActions.removeSuccess, (state, { id }) =>
pokemonAdapter.removeOne(id, state)
),
on(PokemonActions.updateSuccess, (state, { pokemon }) =>
pokemonAdapter.updateOne({ id: pokemon.id, changes: pokemon }, state)
)
);
export function reducer(state: PokemonState | undefined, action: Action) {
return pokemonReducer(state, action);
}
createReducer
메소드 수신 매개변수 목록:
첫 번째 매개 변수는 초기 상태이고, 두 번째 매개 변수는 on
방법의 목록입니다.on
방법에서 첫 번째 매개 변수는 관련 동작이다.내 예에서 나는 데이터 구조를 좋아하기 때문에 조작enum
을 유지했다.물론, 매거를 사용하지 않고 조작을 내보낼 수 있습니다.on
방법의 두 번째 매개 변수는 리셋입니다. 그 중에서 수신state
과payload
.이후에 우리는 강력한 EntityAdapter
을 사용하여 가장 흔히 볼 수 있는 조작을 실행할 수 있다.
결론
본고에서 우리는 @ngrx/entity
패키지의 create*
함수를 사용하여 Pokédex를 재구성했다.create* 함수를 사용하면 응용 프로그램 상태 관리에서 불필요한 복잡성을 줄일 수 있습니다.또한 어댑터는 가장 일반적인 작업(CRUD)을 수행하는 데 사용됩니다.
따라서 이 글에서 우리는 다음과 같은 주제를 포함한다.
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message => of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
import * as PokemonActions from '@states/pokemon/pokemon.actions';
import { Actions, Effect, ofType } from '@ngrx/effects';
import { Observable, of } from 'rxjs';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { PokemonService } from '@services/pokemon.service';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.PokemonActionTypes.ADD_SUCCESS,
PokemonActions.PokemonActionTypes.UPDATE_SUCCESS,
PokemonActions.PokemonActionTypes.DELETE_SUCCESS,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_SUCCESS
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.PokemonActionTypes.ADD_FAILED,
PokemonActions.PokemonActionTypes.UPDATE_FAILED,
PokemonActions.PokemonActionTypes.DELETE_FAILED,
PokemonActions.PokemonActionTypes.LOAD_POKEMONS_FAILED
];
@Effect()
loadAllPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.LOAD_POKEMONS),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(response => new PokemonActions.LoadPokemonSuccess(response)),
catchError(error => of(new PokemonActions.LoadPokemonFailed(error)))
)
)
);
@Effect()
addPokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.ADD),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => new PokemonActions.AddSuccess(pokemon)),
catchError(error => of(new PokemonActions.AddFailed(error)))
)
)
);
@Effect()
deletePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.DELETE),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => new PokemonActions.DeleteSuccess(id)),
catchError(error => of(new PokemonActions.DeleteFailed(error)))
)
)
);
@Effect()
updatePokemon$: Observable<any> = this.actions$.pipe(
ofType(PokemonActions.PokemonActionTypes.UPDATE),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => new PokemonActions.UpdateSuccess(pokemon)),
catchError(error => of(new PokemonActions.UpdateFailed(error)))
)
)
);
@Effect({ dispatch: false })
successNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
);
@Effect({ dispatch: false })
failedNotification$ = this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
);
}
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { catchError, map, switchMap, tap } from 'rxjs/operators';
import { Injectable } from '@angular/core';
import { MatSnackBar } from '@angular/material';
import { Pokemon } from '@shared/interfaces/pokemon.interface';
import { actions as PokemonActions } from '@states/pokemon/pokemon.actions';
import { PokemonService } from '@services/pokemon.service';
import { of } from 'rxjs';
@Injectable()
export class PokemonEffects {
constructor(
private actions$: Actions,
private pokemonService: PokemonService,
public snackBar: MatSnackBar
) {}
POKEMON_ACTIONS_SUCCESS = [
PokemonActions.addSuccess,
PokemonActions.updateSuccess,
PokemonActions.removeSuccess,
PokemonActions.loadPokemonSuccess
];
POKEMON_ACTIONS_FAILED = [
PokemonActions.addFailed,
PokemonActions.updateFailed,
PokemonActions.removeFailed,
PokemonActions.loadPokemonFailed
];
loadAllPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.loadPokemon),
switchMap(() =>
this.pokemonService.getAll().pipe(
map(pokemons => PokemonActions.loadPokemonSuccess({ pokemons })),
catchError(message =>
of(PokemonActions.loadPokemonFailed({ message }))
)
)
)
)
);
addPokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.add),
switchMap((action: any) =>
this.pokemonService.add(action.pokemon).pipe(
map((pokemon: Pokemon) => PokemonActions.addSuccess({ pokemon })),
catchError(message => of(PokemonActions.addFailed({ message })))
)
)
)
);
deletePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.remove),
switchMap(({ id }) =>
this.pokemonService.delete(id).pipe(
map(() => PokemonActions.removeSuccess({ id })),
catchError(message => of(PokemonActions.removeFailed({ message })))
)
)
)
);
updatePokemon$ = createEffect(() =>
this.actions$.pipe(
ofType(PokemonActions.update),
switchMap(({ pokemon }) =>
this.pokemonService.update(pokemon).pipe(
map(() => PokemonActions.updateSuccess({ pokemon })),
catchError(message => of(PokemonActions.updateFailed(message)))
)
)
)
);
successNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_SUCCESS),
tap(() =>
this.snackBar.open('SUCCESS', 'Operation success', {
duration: 2000
})
)
),
{ dispatch: false }
);
failedNotification$ = createEffect(
() =>
this.actions$.pipe(
ofType(...this.POKEMON_ACTIONS_FAILED),
tap(() =>
this.snackBar.open('FAILED', 'Operation failed', {
duration: 2000
})
)
),
{ dispatch: false }
);
}
새로운
createReducer
방법은 switch
문장을 사용하지 않는 상황에서 감속기를 만들 수 있습니다.동작 유형을 구분하고 새로운 상태 참조를 되돌려주는 새로운 on
방법이 있습니다.또 다른 흥미로운 사실은 감속기에서 처리되지 않은 조작을 위해 기본 상황을 처리할 필요가 없다는 것이다.따라서 전후
pokemon.reducers.ts
:이전
import { PokemonActionTypes, PokemonActions } from './pokemon.actions';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
export function pokemonReducer(
state: PokemonState = pokemonInitialState(),
action: PokemonActions
): PokemonState {
switch (action.type) {
case PokemonActionTypes.LOAD_POKEMONS_SUCCESS:
return pokemonAdapter.addAll(action.payload, state);
case PokemonActionTypes.ADD_SUCCESS:
return pokemonAdapter.addOne(action.pokemon, state);
case PokemonActionTypes.DELETE_SUCCESS:
return pokemonAdapter.removeOne(action.id, state);
case PokemonActionTypes.UPDATE_SUCCESS:
const { id } = action.pokemon;
return pokemonAdapter.updateOne(
{
id,
changes: action.pokemon
},
state
);
default:
return state;
}
}
이후import { Action, createReducer, on } from '@ngrx/store';
import { PokemonState, pokemonAdapter } from './pokemon.adapter';
import { actions as PokemonActions } from './pokemon.actions';
export function pokemonInitialState(): PokemonState {
return pokemonAdapter.getInitialState();
}
const pokemonReducer = createReducer(
pokemonInitialState(),
on(PokemonActions.loadPokemonSuccess, (state, { pokemons }) =>
pokemonAdapter.addAll(pokemons, state)
),
on(PokemonActions.addSuccess, (state, { pokemon }) =>
pokemonAdapter.addOne(pokemon, state)
),
on(PokemonActions.removeSuccess, (state, { id }) =>
pokemonAdapter.removeOne(id, state)
),
on(PokemonActions.updateSuccess, (state, { pokemon }) =>
pokemonAdapter.updateOne({ id: pokemon.id, changes: pokemon }, state)
)
);
export function reducer(state: PokemonState | undefined, action: Action) {
return pokemonReducer(state, action);
}
createReducer
메소드 수신 매개변수 목록:첫 번째 매개 변수는 초기 상태이고, 두 번째 매개 변수는
on
방법의 목록입니다.on
방법에서 첫 번째 매개 변수는 관련 동작이다.내 예에서 나는 데이터 구조를 좋아하기 때문에 조작enum
을 유지했다.물론, 매거를 사용하지 않고 조작을 내보낼 수 있습니다.on
방법의 두 번째 매개 변수는 리셋입니다. 그 중에서 수신state
과payload
.이후에 우리는 강력한 EntityAdapter
을 사용하여 가장 흔히 볼 수 있는 조작을 실행할 수 있다.결론
본고에서 우리는 @ngrx/entity
패키지의 create*
함수를 사용하여 Pokédex를 재구성했다.create* 함수를 사용하면 응용 프로그램 상태 관리에서 불필요한 복잡성을 줄일 수 있습니다.또한 어댑터는 가장 일반적인 작업(CRUD)을 수행하는 데 사용됩니다.
따라서 이 글에서 우리는 다음과 같은 주제를 포함한다.
@ngrx/entity
효과와 동작을 자동으로 생성하고reduce 기능을 간소화합니다.@ngrx/data
패키지를 통해 사용됩니다.갈수록 많아져...
Reference
이 문제에 관하여(제3부분.Pokédex 구축:create* 함수를 사용하여 NgRX 개선), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/angular/part-3-build-your-pokedex-improve-ngrx-using-create-functions-2kj4텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)