NgRx/Store
@ngrx/store是基于RxJS的狀態(tài)管理庫。在NgRx中,狀態(tài)是由一個包含action和reducer的函數(shù)的映射組成的。Reducer函數(shù)經(jīng)由action的分發(fā)以及當前或初始的狀態(tài)而被調(diào)用,最后由reducer返回一個不可變的狀態(tài)。
Action: Action是狀態(tài)的改變。它描述了某個事件的發(fā)生,但是沒有指定應(yīng)用的狀態(tài)如何改變。
ActionReducerMap: ActionReducerMap注冊了一系列的reducer,在應(yīng)用中使用StoreModule對它進行配置。
ActionReducer: 它被用于創(chuàng)建reducer,例如logger。
MetaReducer: 在應(yīng)用中使用StoreModule配置的MetaReducer構(gòu)成了根的meta-reducer。
StoreModule: StoreModule是@ngrx/storeAPI中的一個模塊,它被用來在應(yīng)用模塊中配置reducer。
createFeatureSelector: 它為狀態(tài)(state)創(chuàng)建一個feature selector。
createSelector: 它創(chuàng)建一個selector用于生成一個指定的狀態(tài)。
Store: 它提供了Store.select()和Store.dispatch()來與reducer協(xié)同工作。Store.select()用于選擇一個selector,Store.dispatch()用于向reducer分發(fā)action的類型。
一般使用Action、Store、以及一個自定義的State即可。
安裝
npm安裝即可,npm i @ngrx/store --save
創(chuàng)建項目中需要管理的狀態(tài)(xx.state.ts)
State是一個單獨的不可變的數(shù)據(jù)結(jié)構(gòu)(可以理解為一個全局的共享數(shù)據(jù)集)。
export interface XXState {
isXX: boolean; // 某某狀態(tài)
yyArr: Array<any> // 某個數(shù)據(jù)數(shù)組
}
State通過Store.dispatch()一個action進行變更。
創(chuàng)建項目中需要的Action類(xx.action.ts)
NgRx的Action描述了狀態(tài)的變化。對于每一個action,我們都需要創(chuàng)建一個繼承自Action的類,同時定義其type和payload(payload是個可選參數(shù))。
export const XX = 'xx';
export const YY = 'yy';
// 每個action其實就定義了一個類型,action的類型
export class ChangeXXAction implements Action {
readonly type = XX;
constructor() {}
}
export class ChangeYYAction implements Action {
readonly type = YY;
constructor(public payload: any) {}
}
// 導(dǎo)出對應(yīng)的actions
export type XXActions = ChangeXXAction |
ChangeYYAction;
創(chuàng)建項目中需要的Reducer類(xx.reducer.ts)
Reducer描述了任何一個action所對應(yīng)的應(yīng)用的state將怎樣變化。
// 引入action中定義的變量
import * as xAction from './xx.action';
// 默認的初始數(shù)據(jù),在狀態(tài)改變過程中新狀態(tài)將會覆蓋默認數(shù)據(jù)
const initialState: XXState = {
isXX: false,
yyArr: ['DATA1','DATA2']
};
export function reducer(state = initialState, action:XXActions): XXState {
switch(action.type) {
case xAction.XX: {
// ES2018的展開運算符合并對象,新的同key數(shù)據(jù)將會替換舊的
// 否則就會添加新的key-value
return {...initialState,isXX: true,yyArr: ['DATA3']};
}
case xAction.YY: {
return {...initialState,isXX:payload.isXX,yyArr:payload.yyArr};
}
default: {
return state;
}
}
}
使用createSelector()創(chuàng)建選擇器,用于綁定store數(shù)據(jù)
首先需要創(chuàng)建一個索引文件(index.ts(名稱任意))用來管理所有的State:
export interface AppState {
xx: XXState
// 如果要管理多個狀態(tài),在這個接口中添加即可
}
// 引入需要的reducer
import * as xxReducer from './xx.reducer';
// 創(chuàng)建Action和Reducer間的映射關(guān)系(那個action走哪個reducer)
export const reducers: ActionReducerMap<AppState> = {
xx: xxReducer.reducer,
};
// @ngrx/store默認使用 combineReducers創(chuàng)建根meta-reducer。
// 這里調(diào)用combineReducers即可
const developmentReducer = combineReducers(reducers);
// 如果在app.module.ts中不顯示聲明metaReducer的配置項目,那么這里可以省略
export function metaReducer( state: any, action: any) {
return developmentReducer(state, action);
}
// 創(chuàng)建選擇器對應(yīng)選擇的狀態(tài)類型
export const getStateType = (state: AppState) => state.xx;
// 創(chuàng)建要監(jiān)視的狀態(tài)(猜測)
export const getXX = (state: XXState) => state.isXX;
// 創(chuàng)建選擇器(一個選擇器只能監(jiān)視一個State接口中的屬性)
export const getXXState = createSelector(
getStateType,
getXX
);
在app.module.ts中配置store:
imports: [
...
StoreModule.forRoot(reducers), // 上文中的reducers,需要引入
...
],
在需要訂閱數(shù)據(jù)的地方訂閱數(shù)據(jù)(訂閱后會自動更新):
import * as stateRoot from './states/index';
import { Subscription } from 'rxjs';
private subScriptions: Array<Subscription> = [];
...
// 注入stroe
constructor(private store: Store<stateRoot.AppState>) ...
...
...
someFunc():void {
// 訂閱
// 這里返回的是Observable對象,需要進一步subscribe才能獲取數(shù)據(jù)
// 或者在前臺使用"this.isXX$ | async"方式訪問Observable對象
// 前臺直接訪問的Observable對象變量一般后綴$
subScriptions.push(this.store.select(stateRoot.getXXState).subscribe(value => this.isXX = value));
}
最終在銷毀組件的時候取消訂閱:
ngOnDestroy() {
this.subScriptions.forEach(subScription => {
subScription.unsubscribe();
});
}