NgRx 상태를 새로 고침 상태로 유지하는 방법

컨텐트
Persist with Redux DevTools
Re-Hydration Meta-Reducer
Re-Hydration Meta-Reducer + Effects
이것은 각도 애플리케이션이 재부팅될 때 다시 로드할 수 있도록 NgRx 상태를 유지해야 하는 일반적인 요구 사항입니다.필드 데이터로 빈 대상을 채우는 과정을 재수합이라고 한다.저장 데이터를 브라우저 저장소 localStorage 로 영구화하는 것은 흔하지만 서버 측 캐시에서 데이터를 다시 얻을 수도 있다.

📕 I've written a book on NgRx. Learn how to structure your state, write testable reducers and work with actions and effects from one well-crafted resource.


이 모드를 적용할 때는 트랩을 주의해야 합니다.우선, 민감한 데이터를 안전하지 않을 수 있는 저장소에 저장하지 않도록 주의해야 합니다.여러 사용자가 같은 기계에서 일하는 등 요소를 고려하다.또한 스토리지 상태가 만료될 수 있습니다.따라서 검증과 부분재수합 등 기술을 채택할 수 있습니다.
또한 프로그램 상태의 모양이 버전에 따라 달라질 수 있다는 것을 기억하십시오.또한, 고객들은 자신의 저장소에 오래된 버전을 가지고 있을 것이다. 실수로 물을 다시 채우면 응용 프로그램을 파괴할 수도 있다.가능한 해결 방안은 특정 버전을 추적하거나 상태 키를 깊이 있게 검사하는 것을 포함할 수 있습니다.결과에 따라 서열화 상태를 포기하거나 이전할 수 있습니다.
마지막으로, 페이지를 새로 고치는 것이 보통 프로그램을 리셋하는 필수적인 길임을 고려해야 한다.따라서 사용자를 손상 상태로 잠그지 않도록 주의하십시오.
본 사례에 대해 우리는 간소화된 해결 방안을 개발하여 전체 뿌리 상태를 localStorage 에 저장할 것이다.

Redux 개발 도구의 지속적인 사용


Example on StackBlitz
만약 당신이 단지 개발 목적으로만 이 기능을 원한다면, 손가락질할 필요가 없다. 그것은 이미 내장되어 있다.브라우저에 마운트 항목Redux DevTools을 설치하고 @ngrx/store-devtools을 사용하여 응용 프로그램 상점을 검사할 때, 페이지 재마운트 사이에 상태와 조작 기록을 보존할 수 있습니다.
다음은 실제 상황입니다.

사용자에게 브라우저 확장을 설치할 것을 요구할 수 없습니다.따라서 상점을 다시 촉촉하게 하고 싶다면 개발자 체험뿐만 아니라 사용자 체험도 개선하고 싶다면 계속 읽어보세요.

Join my mailing list and follow me on Twitter for more in-depth Angular knowledge


재수합 편환원제


Example on StackBlitz
재수합 작용을 실시하는 상용 방법은 meta-reducers에 근거한다.이런 재수화원 환원제는 반드시 두 가지 일을 해야 한다.
  • 실제 감속기
  • 에서 각 동작을 처리한 후 결과 상태를 유지한다
  • 초기화 시 지속성 제공
  • 메타데이터 축소기 내부에서 지속화 결과 상태는 매우 간단합니다. 우리는 상태 대상을 JSON으로 서열화하여 localStorage 에 넣었습니다.상태를 서열화할 수 있다는 것을 알았을 때, 이것은 곧 일을 할 수 있을 것이다.
    또한 NgRx는 정의되지 않은 상태와 INIT 작업을 사용하여 Reducer를 호출하여 초기 상태를 검색합니다.이것은 기본 Reducer의 초기 상태가 아니라 존재할 수 있는 저장 상태를 분석하고 되돌아오는 곳입니다.다음은 해당 메타 감속기의 모양입니다.
    // hydration.reducer.ts
    import { ActionReducer, INIT } from "@ngrx/store";
    import { RootState } from "..";
    
    export const hydrationMetaReducer = (
      reducer: ActionReducer<RootState>
    ): ActionReducer<RootState> => {
      return (state, action) => {
        if (action.type === INIT) {
          const storageValue = localStorage.getItem("state");
          if (storageValue) {
            try {
              return JSON.parse(storageValue);
            } catch {
              localStorage.removeItem("state");
            }
          }
        }
        const nextState = reducer(state, action);
        localStorage.setItem("state", JSON.stringify(nextState));
        return nextState;
      };
    };
    
    메모리에 잘못된 데이터가 있을 때 복구할 수 있도록try-catch 블록에 분석을 포장할 것입니다.
    전체 스토어의 수분을 다시 보충하기 위해 루트 디렉토리에 meta reducer를 등록해야 합니다.
    // index.ts
    import { MetaReducer } from "@ngrx/store";
    import { hydrationMetaReducer } from "./hydration.reducer";
    
    export const metaReducers: MetaReducer[] = [hydrationMetaReducer];
    
    // app.module.ts
    import { StoreModule } from '@ngrx/store';
    import { reducers, metaReducers } from './store';
    
    @NgModule({
      imports: [
        StoreModule.forRoot(reducers, { metaReducers })
      ]
    })
    
    ngrx-store-localstorage 라는 유명한 라이브러리가 있는데, 이 라이브러리를 이용하여 저장소를 로컬 저장소에 동기화할 수 있습니다.이것은 이런 간단한 메타 리듀서 방법을 이용했고 사용자 정의보다 우수한 장점을 제공했다.

    Got stuck? Post a comment below or ping me on Twitter


    재수합 편환원제 + 효과


    Example on StackBlitz
    서열화, 해석, 지속화는 나에게 분명히 부작용이다.단지 JSON.stringify() , JSON.parse() localStorage 가 동기화 API이기 때문에 순수하다는 것을 의미하지는 않는다.감속기(또는 메타 감속기)에 넣는 것 자체가 NgRx 원칙에 어긋난다.이것은 결코 이런 방식으로 재수합을 실현하는 것을 허락하지 않는다는 것을 의미하지는 않지만, 다른 방법의 가치가 있을 수 있다
    NgRx 기반 빌드 블록의 재수합 작용에 대해 다시 생각해 보겠습니다.브라우저 API와의 상호 작용이 유효해야 합니다.그러나 효과상 상태를 설정하는 것은 불가능하기 때문에 우리는 여전히 감속기가 필요하거나, 더 정확히 말하면 원 감속기가 필요하다.그것은 효과가 내는 동작에 따라 상태를 유지할 뿐이다.
    먼저 수화 작업을 시작하는 작업과 스토리지 상태를 검색할 수 있는지 여부를 나타내는 두 가지 추가 작업을 정의합니다.
    // hydration.actions.ts
    import { createAction, props } from "@ngrx/store";
    import { RootState } from "..";
    
    export const hydrate = createAction("[Hydration] Hydrate");
    
    export const hydrateSuccess = createAction(
      "[Hydration] Hydrate Success",
      props<{ state: RootState }>()
    );
    
    export const hydrateFailure = createAction("[Hydration] Hydrate Failure");
    
    Google Meta reducer는 매우 간단하기 때문에 순수함을 유지할 수 있습니다. hydrateSuccess 기반 작업의 상태만 바꾸면 됩니다.다른 어떤 상황에서도, 그것은 밑바닥의 Reducer를 실행할 것이다.
    // hydration.reducer.ts
    import { Action, ActionReducer } from "@ngrx/store";
    import * as HydrationActions from "./hydration.actions";
    import { RootState } from "..";
    
    function isHydrateSuccess(
      action: Action
    ): action is ReturnType<typeof HydrationActions.hydrateSuccess> {
      return action.type === HydrationActions.hydrateSuccess.type;
    }
    
    export const hydrationMetaReducer = (
      reducer: ActionReducer<RootState>
    ): ActionReducer<RootState> => {
      return (state, action) => {
        if (isHydrateSuccess(action)) {
          return action.state;
        } else {
          return reducer(state, action);
        }
      };
    };
    
    isHydrateSuccess()helper 함수 실현user-defined type guard.이렇게 하면 우리는 state의 조작 유형을 바탕으로 안전하게 hydrateSuccess 유효 부하 속성에 접근할 수 있다.
    현재 우리는 hydrateSuccess 에서 사용할 수 있는 서열화 상태에 따라 분파 hydrateFailurelocalStorage 조작의 효과를 작성할 수 있다.이것은 hydrate 작업으로 시작되며 OnInitEffects 생명 주기에 이 작업을 되돌려줍니다.그 다음에, 우리는 상수 키 "state" 를 사용하여 메모리에서 값을 검색하여 그것을 분석하고 해당하는 조작을 되돌려 보려고 한다.상태를 성공적으로 해석하면 meta reducer에서 끝나 NgRx 저장소에 저장됩니다.
    // hydration.effects.ts
    import { Injectable } from "@angular/core";
    import { Actions, createEffect, ofType, OnInitEffects } from "@ngrx/effects";
    import { Action, Store } from "@ngrx/store";
    import { distinctUntilChanged, map, switchMap, tap } from "rxjs/operators";
    import { RootState } from "..";
    import * as HydrationActions from "./hydration.actions";
    
    @Injectable()
    export class HydrationEffects implements OnInitEffects {
      hydrate$ = createEffect(() =>
        this.action$.pipe(
          ofType(HydrationActions.hydrate),
          map(() => {
            const storageValue = localStorage.getItem("state");
            if (storageValue) {
              try {
                const state = JSON.parse(storageValue);
                return HydrationActions.hydrateSuccess({ state });
              } catch {
                localStorage.removeItem("state");
              }
            }
            return HydrationActions.hydrateFailure();
          })
        )
      );
    
      constructor(private action$: Actions, private store: Store<RootState>) {}
    
      ngrxOnInitEffects(): Action {
        return HydrationActions.hydrate();
      }
    }
    
    여전히 부족한 것은 효과가 있다. 우선 현재 상태를 localStorage 까지 지속할 것이다.우리는 액션스 흐름을 바탕으로 기다릴 것이다. hydrateSuccess 또는 hydrateFailure.이렇게 하면 우리는 다시 물을 합치기 전에 기존 상태를 덮지 않을 것이다.그리고 우리는 더 이상 조작을 보지 않고 switchMap() 조작원을 통해 상점을 구독했다.위에 distinctUntilChanged() 를 치면 수시로 상태를 바꾸는 흐름이 있을 것이다.마지막으로 우리는 효과를 non-dispatching로 표시하고 상태를 localStorage 조작부호 내부 tap() 로 서열화했다.
    // hydration.effects.ts
    serialize$ = createEffect(
      () =>
        this.action$.pipe(
          ofType(HydrationActions.hydrateSuccess, HydrationActions.hydrateFailure),
          switchMap(() => this.store),
          distinctUntilChanged(),
          tap((state) => localStorage.setItem("state", JSON.stringify(state)))
        ),
      { dispatch: false }
    );
    
    모듈 성명에 새 effect 클래스를 등록하는 것을 잊지 마세요.그 밖에 injecting the localStorage 와/또는 전체 해석과 지속화 과정을 다른 서비스로 하청하는 것이 가장 좋다.
    NgRx 원칙을 준수하는 것 외에 이러한 효과에 기초한 재수합 실시는 우리를 허용한다
  • 의존 주입을 이용하여 테스트를 간소화
  • 시간 기반 필터링 포함(예: RxJS 연산자, 예: auditTime() )
  • 고급 오류 처리 수행
  • 비동기원
  • 에서 재수합물
    유일한 단점은 초기 상태를 직접 교체할 수 있는 저장 상태를 제공할 수 없다는 것이다.만약 이것이 요구라면, 불순한 실현을 피하기 위해 register reducers via dependency injection 시도할 수 있습니다.

    좋은 웹페이지 즐겨찾기