如何使用Reducers为多个资源/组件组合AppState?

时间:2017-06-12 18:57:03

标签: ngrx

我正在试图弄清楚如何将许多资源状态组合成各种组件状态以及构成AppState的内容。大多数ngrx指南/示例只处理资源(例如书籍)或有限状态(例如书籍和选定的书籍),但我认为我没有遇到过比这更复杂的事情。

当你有十几个资源,你需要不同资源状态的多个组件中有各种状态(列表,项目,搜索条件,菜单项,过滤器等)时,你会怎么做?

我已经四处寻找,我想出了以下结构,但我不相信这是预期的结果:

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

您可以将资源缩减器(例如bookReducer,booksReducer,bookSearchTitleReducer)组合到与组件相关的缩减器(例如bookSearchReducer)中,然后将所有组件缩减器合并到一个具有一个AppState的缩减器中,并在AppModule中将其与商店提供程序一起使用。

这是要走的路还是还有其他(正确的)方法吗?如果这是一个很好的方法,我会在Component构造函数中使用Store或Store吗?

[编辑]

好的,ngrx-example-app确实处理了更多组件,我看到它只在组件级创建状态,而不是在资源级创建状态,组合状态和各自的reducers并在组件构造函数中使用完整的状态对象: 'store:Store'。

我想,因为这是一个官方的例子,这将是处理州/减少者的预期方式。

1 个答案:

答案 0 :(得分:8)

[编辑]

新的v4 ngrx使用起来更简单,有更好的文档和示例应用可以帮助您。以下内容主要与v2及其怪癖相关,后者在v4中不再是问题。

[作废]

经过大量的反复试验,我找到了一个好的,有效的配方。我将在这里分享它的要点,也许它会帮助某人。

减速器组成指南对我帮助很大,并说服我选择资源的原始状态/减速器结构&gt;组分&gt;应用。该指南太大而不适合此处,您可能需要最新版本here

这是一个关于具有两个组件的应用程序的一些关键文件中我必须做的快速浏览,其中包含两个基本资源(用户和资产)以及衍生(列表)和参数(搜索)。

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文件,它将来会导出redurs,所有模型以及其他一些东西。

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}}等内容进行测试。

就是这样。可能有更好的方法,我知道我可以只用一个级别(例如只是基本资源),这完全取决于你认为最适合你的应用程序。