[anguar2]redux與rxjs的結合,使用NgRx來管理component間的溝通還有追蹤改變狀態

筆記一下有關ngrx的配置方式,還有個人的一些想法

前言

redux在前端現在可說是無人不知無人不曉,Rx系列也是越來越多技術採用了,不管是Swift甚至是android都有相關對應rx的實做,筆者因為有從無到有實作過vuex,也有從無到有實作過react+redux+redux thunk,每種實現方式都有點不同,也有各自技術的限制,在這方面其實ngRx的限制很少,甚至在angular2根本不需要redux也能有很多辦法可以做到組件的溝通,所以這只是一個選擇性方案,並不是必要的,那難道我只是為了研究技術的心態來做ngRx嗎,當然筆者沒有那麼無聊,畢竟要學的東西還有非常多,使用ngRx最重要的目的,就是redux devtool了,可以方便我們有工具幫忙可以trace state

 

導覽

  1. Angular2組件溝通的幾種方式
  2. Redux的一些簡單說明
  3. NgRx和Redux的一點差異
  4. NgRx的安裝
  5. Component的結合
  6. 比較複雜的應用
  7. 結論

 

 

Angular2組件溝通的幾種方式

其實angular2有非常多的方式,當然在組件溝通的時候,我們必須意識到並不是所有都得使用ngrx來實做,我們可能必須得考慮到通用性的部份,也就是我們如果做為一個通用的元件,這個元件應該以最少知識原則來看待,盡量把所有的knowleage交由調用方來實做,其實最簡單的就是@input@output了,接著我們還可以延用angular1service溝通的方式,來讓組件間溝通,或者我們也可以使用rxjssubject來實現,不過對我來說有reduxdevtool的監控,在組件間的溝通,我還是選擇以ngRx為方案。

 

 

Redux的一些簡單說明

Redux其實就是狀態管理機制,也就是從Flux的啟發而改良開發出來的,原因是因為react其實是完全的one way binding,所以要畫面重刷的話,必須都得自己手動去更新state,但是如果我們畫面非常複雜,元件拆得非常細的話,手動更新state其實也是一個挺大的負擔,如果用redux的話,我們只要調用action去通知reducer的話,store一有改變,就會自己去觸發畫面改變,所以在react的世界裡面,redux幾乎可說是必用方案,而不是可選方案了,這就造成了其實所有的邏輯都會放在reducer裡面,然後我們觸發ajax的時候必須要在有一個middleware去處理非同步的部份,不然的話react-redux會直接報錯,基本介紹redux就到這邊了。

 

 

NgRxRedux的一點差異

ngRx?ngRx也有實做middleware,叫做是ngrx effects,但在ngrx其實操作非同步,不使用ngrx effects也不會有問題,馬照跑舞照跳,我個人比較喜歡的方式,是把邏輯放在service層實做,包括ajax的調用,當我們做完某段邏輯,想要去通知組件更新的時候,再去調用action觸發reducer來實做就可以了,而且需注意NgRx的註冊方式一樣是回傳一個Observable哦,我們一樣可以使用subscribe的方式來觸發訂閱,接下來就是一些安裝和說明囉

 

 

NgRx的安裝

首先照著官網的說明,就是先安裝npm install @ngrx/core @ngrx/store --save,接著因為我需要redux的devtool來協助追蹤狀態,所以我需要再安裝 npm install @ngrx/store-devtools --save,然後我習慣在src/app的目錄夾裡面新增一個stores的目錄,來區分這個是redux相關的,因為redux強調只有一個store的觀念,實際上我們可以以自己的區分來做多個reducer最後再對應回一個store,這邊也是看個人的自由發揮,在store目錄夾底下,我新增了一支action-type.ts(定義一些對應reducer的actoin的常數)和root-reducer.ts (主要的reducer),在此我還是以一個最簡單的count來做示例,先在actoin-type.ts新增常數

src/app/store/action-type.ts

export const INCREASE_COUNT = 'INCREASE_COUNT';
export const REDUCE_COUNT = 'REDUCE_COUNT';

接著就直接新增reducer的邏輯了

import { INCREASE_COUNT, REDUCE_COUNT } from './action-type';
import { Action } from '@ngrx/store';

export interface RootState {
  count: number;
};

export const initialRootState: RootState = {
  count: 0
};

export function rootReducer(state = initialRootState, action: Action): RootState {
  switch (action.type) {
    case INCREASE_COUNT:
      state.count++;
      return Object.assign({}, state); //這邊得注意一下,這邊就算直接return state也不會出錯,但在redux devtool的時候,追蹤狀態就沒有效果了,主要還是pure function的概念,防止side effect。
    case REDUCE_COUNT:
      state.count--;
      return Object.assign({}, state);
    default:
      return Object.assign({}, state);
  }
}

接著在app.module.ts裡面,import需要的module

 imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    StoreModule.provideStore({ rootReducer: rootReducer }), //除了註冊store也要加進rootReducer,這邊不要使用一個rootReducer就好,因為筆者嘗試在build --prod的時候,會有問題
    StoreDevtoolsModule.instrumentOnlyWithExtension() //這邊是註冊redux的devtool
  ],

NgRx與component結合

接著我在component需要在construct的部份,選擇我們的store和對應的reducer了

/src/app/app.component.ts

import { INCREASE_COUNT, REDUCE_COUNT } from './store/action-type';
import { RootState } from './store/root-reducer';
import { Store } from '@ngrx/store';
import { Component } from '@angular/core';
import { Observable } from "rxjs/Observable";

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  rootStore$: Observable<RootState>;

  //下面的RootState是我們定義在RootReducer的state哦
  constructor(private store: Store<RootState>) {
    this.rootStore$ = store.select('rootReducer'); //這邊則是我們定義在app module裡面的reducer名稱哦
  }

  plus() {
    this.store.dispatch({ type: INCREASE_COUNT });
  }

  reduce() {
    this.store.dispatch({ type: REDUCE_COUNT });
  }
}

/src/app/app.component.html

<h1>
  {{(rootStore$ | async).count}}
</h1>
<button (click)="plus()">+1</button>
<button (click)="reduce()">-1</button>

最後我們必須要安裝redux的devtool,請自行為chrome安裝,然後就可以開啟redux devtool來監控state的變化了

比較複雜的應用

其實這個示例是直接在component就調用了,如果你是把邏輯都寫在recuder而且有調用effects的話,確實是可以這樣子做,component只要觸發一個action,但是因為我個人的選擇是對service來封裝邏輯和調用ajax,所以我在調用dispatch的部份,幾乎都是在service裡面調用,而且必須要注意,並不是所有的東西都會去使用到NgRx,我個人是只有針對組件間的溝通,才會使用NgRx來操作,還有如果一旦我們使用NgRx調用溝通,我們要把通用性的元件搬去另一個專案使用,就會很難重覆使用,因為我們同時候還要搬動store的部份,甚至還要搬動service,而且另一個專案也必須得安裝NgRx,所以什麼樣的組件要使用NgRx來管理狀態,甚至是不是真的需要使用NgRx,這方面需要好好規劃和思考。

結論

因為angular2有太多方式可以做元件間的溝通,所以每個團隊的實做方式都不一定相同,但提供了以redux的概念方式來管理狀態,我個人覺得這是一種比較完美的選擇,以上如果有什麼建議或覺得筆者有任何錯誤的地方,再請多多指導。