web-dev-qa-db-ja.com

複数のリソース/コンポーネントに対して、Reducerを使用してAppStateをどのように構成しますか?

多くのリソース状態をさまざまなコンポーネント状態に組み合わせる方法と、AppStateの構成要素を理解しようとしています。そこにあるほとんどのngrxガイド/例では、リソース(本など)または限られた状態(本や選択した本など)のみを処理しますが、それ以上複雑なものに出くわしたことはないと思います。

さまざまなリソースの状態を必要とする複数のコンポーネントにさまざまな状態(リスト、アイテム、検索用語、メニュー項目、フィルターなど)がある12個のリソースがある場合はどうしますか?

私は周りを検索して、次の構造を思いつきましたが、これが意図されたものであるとは確信していません:

AppState & reducer
<- combine reducers
- Component states & reducers
<- combine reducers
-- Resource states & reducers

リソースリデューサー(bookReducer、booksReducer、bookSearchTitleReducerなど)をコンポーネントに関連するリデューサー(bookSearchReducerなど)に結合し、すべてのコンポーネントリデューサーを1つのAppStateを持つ1つのリデューサーに結合し、AppModuleでストアプロバイダーを使用します。

これは方法ですか、それとも別の(適切な)方法がありますか?そして、これがそれを行う良い方法である場合、私はStoreまたはStoreをComponentコンストラクターで使用しますか?

[編集]

わかりました、ngrx-example-appはより多くのコンポーネントを処理します。リソースレベルではなくコンポーネントレベルでのみ状態を作成し、状態とそれぞれのレデューサーを組み合わせ、コンポーネントコンストラクターで完全な状態オブジェクトを使用します: 'store:お店'。

これは公式の例なので、これは状態/リデューサーを処理するための意図された方法になると思います。

8
Kesarion

[編集]

新しい v4 ngrx は使用がはるかに簡単で、より優れたドキュメントとサンプルアプリがあり、役立つでしょう。以下は主にv2とその癖に関連していますが、v4では問題ではなくなりました。

[廃止]

多くの試行錯誤の後、私は良い実用的な式を見つけました。その要点をここで共有します。多分それは誰かを助けるでしょう。

レデューサー構成に関するガイドは、私を大いに助け、リソース>コンポーネント>アプリの元の状態/レデューサー構造に進むことを確信しました。ガイドが大きすぎてここに収まらないため、最新バージョン here が必要になる可能性があります。

これは、2つの基本的なリソース(ユーザーとアセット)と派生物(リスト)とパラメーター(検索)を持つ2つのコンポーネントを持つアプリのいくつかの主要ファイルで私がしなければならないことの簡単な要約です。

_store/reducers/user/index.ts:_

_import { ActionReducer, combineReducers } from '@ngrx/store';

import { authenticatedUserReducer } from './authenticatedUser.reducer';
import { selectedUserReducer } from './selectedUser.reducer';
import { userListReducer } from './userList.reducer';
import { userSearchReducer } from './userSearch.reducer';
import { User } from '../../models';

const reducers = {
  authenticated: authenticatedUserReducer,
  selected: selectedUserReducer,
  list: userListReducer,
  search: userSearchReducer
};

interface UserState {
  authenticated: User,
  selected: User,
  list: User[],
  search: string
}

const reducer: ActionReducer<UserState> = combineReducers(reducers);

function userReducer(state: any, action: any) {
  return reducer(state, action);
}

export { userReducer, UserState };
_

_store/reducers/asset/index.ts:_

_import { ActionReducer, combineReducers } from '@ngrx/store';

import { selectedAssetReducer } from './selectedAsset.reducer';
import { assetListReducer } from './assetList.reducer';
import { assetSearchReducer } from './assetSearch.reducer';
import { Asset } from '../../models';

const reducers = {
  selected: selectedAssetReducer,
  list: assetListReducer,
  search: assetSearchReducer
};

interface AssetState {
  selected: Asset,
  list: Asset[],
  search: string
}

const reducer: ActionReducer<AssetState> = combineReducers(reducers);

function assetReducer(state: any, action: any) {
  return reducer(state, action);
}

export { assetReducer, AssetState };
_

_store/reducers/index.ts:_

_import { routerReducer, RouterState } from '@ngrx/router-store';

import { userReducer, UserState } from './user';
import { assetReducer, AssetState } from './asset';

const reducers = {
  router: routerReducer,
  user: userReducer,
  asset: assetReducer
};

interface AppState {
  router: RouterState,
  user: UserState,
  asset: AssetState
}

export { reducers, AppState };
_

注:別売りのルーター減速機も含めました。

_app.module.ts:_

_import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { RouterModule } from '@angular/router';

import { StoreModule } from '@ngrx/store';
import { RouterStoreModule } from '@ngrx/router-store';

import { reducers } from './store';
import { AppComponent } from './app.component';
import { AppRoutes } from './app.routes';
import { HomeComponent } from './components/home/home.component';

@NgModule({
  declarations: [
    AppComponent,
    HomeComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(AppRoutes),
    StoreModule.provideStore(reducers),
    RouterStoreModule.connectRouter()
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }
_

注:ここでは必要なものを使用し、不要なものはスクラップしてください。/store内に別のindex.tsファイルを作成しました。これは、レデューサー、すべてのモデル、そしておそらく将来的には他のいくつかのものをエクスポートします。

_home.component.ts:_

_import { Component } from '@angular/core';

import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';

import { AppState, User } from '../../store';

@Component({
  selector: 'home',
  templateUrl: './home.template.html'
})
export class HomeComponent {
  user: Observable<User>;

  constructor (private store: Store<AppState>) {
    this.user = store.select('user', 'selected');
    store.dispatch({ type: 'SET_USER_NAME', payload: 'Jesse' });
    store.dispatch({ type: 'ADD_USER_ROLE', payload: 'scientist' });
    store.dispatch({ type: 'ADD_USER_ROLE', payload: 'wordsmith' });
  }
}
_

注:テンプレートで{{(user | async)?.name}}などを使用してテストできます。

そして、それはそれについてです。これを行うにはもっと良い方法があるかもしれません。たとえば、1つのレベル(たとえば、基本的なリソースのみ)でそれを作成できたかもしれませんが、それはすべて、アプリに最も適していると思うものによるものです。

9
Kesarion