수색…


소개

Ngrx는 Angular2와 함께 사용할 수있는 강력한 라이브러리입니다. 배후의 아이디어는 예측 가능한 상태 컨테이너 가있는 반응 형 앱 을 가지고 잘 작동하는 두 개념을 병합하는 것입니다. - [Redux] [1] - [RxJs] [2] 주요 이점 : - 구성 요소간에 앱의 데이터 공유 애플리케이션 코어 로직을 테스트하는 것은 Angular2에 대한 의존성없이 순수한 함수를 테스트하는 것으로 구성됩니다. (아주 간단합니다!) [1] : http://redux.js.org [2] : http : // reactivex. io / rxjs

전체 예제 : 사용자 로그인 / 로그 아웃

선결 요건

이 주제는 Redux 및 / 또는 Ngrx에 관한 내용이 아닙니다 .

  • Redux에 익숙해야합니다.
  • 최소한 RxJs와 Observable 패턴의 기초를 이해하십시오.

먼저, 처음부터 예제를 정의하고 몇 가지 코드로 실행 해 봅시다.

개발자로서 저는 다음과 같이하고 싶습니다.

  1. User 의 속성을 정의하는 IUser 인터페이스가 있어야합니다.
  2. 나중에 Store 에서 User 를 조작하기 위해 사용할 액션을 선언하십시오.
  3. UserReducer 의 초기 상태 정의
  4. 감속기 UserReducer 만들기
  5. 우리의 UserReducer 를 메인 모듈로 UserReducer Store 를 만듭니다.
  6. Store 데이터를 사용하여보기에 정보를 표시하십시오.

Spoiler 경고 : 시작하기 전에 데모를 바로 보거나 코드를 읽으려면 Plunkr ( 임베드 뷰 또는 실행 뷰 )을 사용하십시오.

1) IUser 인터페이스 정의

인터페이스를 두 부분으로 나누고 싶습니다.

  • 서버에서 가져올 속성
  • UI에 대해서만 정의한 속성 (버튼이 회전해야하는 경우)

그리고 우리가 사용할 IUser 인터페이스는 다음과 같습니다.

user.interface.ts

export interface IUser {
  // from server
  username: string;
  email: string;
  
  // for UI
  isConnecting: boolean;
  isConnected: boolean;
};

2) User 조작을위한 액션 선언

이제 우리는 감속기 가 어떤 행동을 취해야하는지 생각해야합니다.
여기서 말하자 :

user.actions.ts

export const UserActions = {
  // when the user clicks on login button, before we launch the HTTP request
  // this will allow us to disable the login button during the request
  USR_IS_CONNECTING: 'USR_IS_CONNECTING',
  // this allows us to save the username and email of the user
  // we assume those data were fetched in the previous request
  USR_IS_CONNECTED: 'USR_IS_CONNECTED',

  // same pattern for disconnecting the user
  USR_IS_DISCONNECTING: 'USR_IS_DISCONNECTING',
  USR_IS_DISCONNECTED: 'USR_IS_DISCONNECTED'
};

그러나 우리가 이러한 행동을 사용하기 전에, 우리가 왜 우리를 위해 그러한 행동 중 일부 를 파견 할 서비스가 필요한지 설명해주십시오.

사용자를 연결하려고한다고 가정 해 보겠습니다. 로그인 버튼을 클릭하면 다음과 같이 진행됩니다.

  • 버튼을 클릭하십시오.
  • 구성 요소가 이벤트를 catch하고 userService.login 호출합니다.
  • userService.login 메서드 dispatch store 속성을 업데이트하기 위해 이벤트를 dispatch 합니다. user.isConnecting
  • HTTP 호출이 시작됩니다 ( 비동기 동작 을 시뮬레이트하기 위해 데모에서 setTimeout 을 사용합니다)
  • HTTP 호출이 끝나면 사용자가 기록되었음을 스토어에 알리는 또 다른 작업을 전달합니다.

user.service.ts

@Injectable()
export class UserService {
  constructor(public store$: Store<AppState>) { }

  login(username: string) {
    // first, dispatch an action saying that the user's tyring to connect
    // so we can lock the button until the HTTP request finish
    this.store$.dispatch({ type: UserActions.USR_IS_CONNECTING });

    // simulate some delay like we would have with an HTTP request
    // by using a timeout
    setTimeout(() => {
      // some email (or data) that you'd have get as HTTP response
      let email = `${username}@email.com`;

      this.store$.dispatch({ type: UserActions.USR_IS_CONNECTED, payload: { username, email } });
    }, 2000);
  }

  logout() {
    // first, dispatch an action saying that the user's tyring to connect
    // so we can lock the button until the HTTP request finish
    this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTING });

    // simulate some delay like we would have with an HTTP request
    // by using a timeout
    setTimeout(() => {
      this.store$.dispatch({ type: UserActions.USR_IS_DISCONNECTED });
    }, 2000);
  }
}

3) UserReducer 의 초기 상태 정의

user.state.ts

export const UserFactory: IUser = () => {
  return {
    // from server
    username: null,
    email: null,

    // for UI
    isConnecting: false,
    isConnected: false,
    isDisconnecting: false
  };
};

4) 감속기 UserReducer 만들기

감속기는 2 가지 인수를 취합니다.

  • 현재 상태
  • Action 유형의 Action<{type: string, payload: any}>

알림 : 감속기는 어느 시점에서 초기화해야합니다.

3 장에서 감속기의 기본 상태를 정의 했으므로 다음과 같이 사용할 수 있습니다.

user.reducer.ts

export const UserReducer: ActionReducer<IUser> = (user: IUser, action: Action) => {
  if (user === null) {
    return userFactory();
  }
  
  // ...
}

다행히도 우리의 factory 함수를 사용하여 객체를 반환하고 감속기 내에서 (ES6) 기본 매개 변수 값 을 사용하여이를 작성하는 더 쉬운 방법이 있습니다.

export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
  // ...
}

그런 다음 감속기의 모든 작업을 처리해야합니다. : ES6 Object.assign 함수를 사용하여 상태를 불변으로 유지합니다.

export const UserReducer: ActionReducer<IUser> = (user: IUser = UserFactory(), action: Action) => {
  switch (action.type) {
    case UserActions.USR_IS_CONNECTING:
      return Object.assign({}, user, { isConnecting: true });

    case UserActions.USR_IS_CONNECTED:
      return Object.assign({}, user, { isConnecting: false, isConnected: true, username: action.payload.username });

    case UserActions.USR_IS_DISCONNECTING:
      return Object.assign({}, user, { isDisconnecting: true });

    case UserActions.USR_IS_DISCONNECTED:
      return Object.assign({}, user, { isDisconnecting: false, isConnected: false });

    default:
      return user;
  }
};

5) 우리의 UserReducer 를 메인 모듈로 UserReducer Store 를 구축하십시오

app.module.ts

@NgModule({
    declarations: [
    AppComponent
    ],
    imports: [
    // angular modules
    // ...

    // declare your store by providing your reducers
    // (every reducer should return a default state)
    StoreModule.provideStore({
        user: UserReducer,
        // of course, you can put as many reducers here as you want
        // ...
    }),

    // other modules to import
    // ...
    ]
});

6) Store 데이터를 사용하여 정보를 표시합니다.

이제 모든 것이 로직 측면에서 준비가되었으며 우리가 원하는 것을 두 가지 구성 요소로 표시하면됩니다.

  • UserComponent : [덤 구성 요소] @Input 속성과 async 파이프를 사용하여 상점에서 사용자 개체를 전달합니다. 이렇게하면 구성 요소는 사용 가능한 경우에만 사용자를받습니다 ( userObservable<IUser> 유형이 아닌 IUser 유형이됩니다).
  • LoginComponent [Smart component] 우리는 Store 를이 구성 요소에 직접 삽입하고 Observable 로서 user 에게만 작업합니다.

user.component.ts

@Component({
  selector: 'user',
  styles: [
    '.table { max-width: 250px; }',
    '.truthy { color: green; font-weight: bold; }',
    '.falsy { color: red; }'
  ],
  template: `
    <h2>User information :</h2>

    <table class="table">
      <tr>
        <th>Property</th>
        <th>Value</th>
      </tr>

      <tr>
        <td>username</td>
        <td [class.truthy]="user.username" [class.falsy]="!user.username">
          {{ user.username ? user.username : 'null' }}
        </td>
      </tr>

      <tr>
        <td>email</td>
        <td [class.truthy]="user.email" [class.falsy]="!user.email">
          {{ user.email ? user.email : 'null' }}
        </td>
      </tr>

      <tr>
        <td>isConnecting</td>
        <td [class.truthy]="user.isConnecting" [class.falsy]="!user.isConnecting">
          {{ user.isConnecting }}
        </td>
      </tr>

      <tr>
        <td>isConnected</td>
        <td [class.truthy]="user.isConnected" [class.falsy]="!user.isConnected">
          {{ user.isConnected }}
        </td>
      </tr>

      <tr>
        <td>isDisconnecting</td>
        <td [class.truthy]="user.isDisconnecting" [class.falsy]="!user.isDisconnecting">
          {{ user.isDisconnecting }}
        </td>
      </tr>
    </table>
  `
})
export class UserComponent {
  @Input() user;

  constructor() { }
}

login.component.ts

@Component({
  selector: 'login',
  template: `
    <form
      *ngIf="!(user | async).isConnected"
      #loginForm="ngForm"
      (ngSubmit)="login(loginForm.value.username)"
    >
      <input
        type="text"
        name="username"
        placeholder="Username"
        [disabled]="(user | async).isConnecting"
        ngModel
      >
 
      <button
        type="submit"
        [disabled]="(user | async).isConnecting || (user | async).isConnected"
      >Log me in</button>
    </form>
 
    <button
      *ngIf="(user | async).isConnected"
      (click)="logout()"
      [disabled]="(user | async).isDisconnecting"
    >Log me out</button>
  `
})
export class LoginComponent {
  public user: Observable<IUser>;
 
  constructor(public store$: Store<AppState>, private userService: UserService) {
      this.user = store$.select('user');
  }
 
  login(username: string) {
    this.userService.login(username);
  }
 
  logout() {
    this.userService.logout();
  }
}

NgrxReduxRxJs 개념을 통합 한 Ngrx 때문에 Ngrx 에는 RxJs 이해하는 것이 매우 어려울 수 있습니다. 그러나 이것은이 예에서 알 수 있듯이 반응 형 앱을 제공 하고 데이터를 쉽게 공유 할 수있는 강력한 패턴입니다. Plunkr을 사용할 수 있다는 것을 잊지 말고 자신의 테스트를 위해 포크로 만들 수 있습니다!

나는 그것이 tho 주제가 아주 길다는 것에 도움이 되었기를 바랍니다, 환호!



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow