NgRx/Store详解及实现

NgRx/Store 4和Angular5

今天看到了一篇文章关于ngrx/store的实现,感觉讲的非常详细,所以转发支持下。

@ngrx/store是基于RxJS的状态管理库。在NgRx中,状态是由一个包含action和reducer的函数的映射组成的。Reducer函数经由action的分发以及当前或初始的状态而被调用,最后由reducer返回一个不可变的状态。

Action: Action是状态的改变。它描述了某个事件的发生,但是没有指定应用的状态如何改变。

ActionReducerMapActionReducerMap注册了一系列的reducer,在应用中使用StoreModule对它进行配置。

ActionReducer: 它被用于创建reducer,例如logger。

MetaReducer: 在应用中使用StoreModule配置的MetaReducer构成了根的meta-reducer。

StoreModule: StoreModule@ngrx/storeAPI中的一个模块,它被用来在应用模块中配置reducer。

createFeatureSelector: 它为状态(state)创建一个feature selector。

createSelector: 它创建一个selector用于生成一个指定的状态。

Store: 它提供了Store.select()Store.dispatch()来与reducer协同工作。Store.select()用于选择一个selector,Store.dispatch()用于向reducer分发action的类型。

1. 使用到的技术

下面是我们的例子中用到的技术:

  1. Angular 5.0.0

  2. Angular CLI 1.5.0

  3. NgRx/Store 4.1.1

  4. TypeScript 2.4.2

  5. Node.js 6.11.0

  6. NPM 3.10.10

2. 安装Angular CLI和NgRX/Store

  1. 确保已安装的Node和NPM的最低版本为:Node 6.9.x和NPM 3.x.x

  2. 在命令行中输入以下命令:

npm install -g @angular/cli
  1. 运行以下命令生成一个新的项目:

ng new my-app
  1. 现在安装@ngrx/store。使用命令行进入my-app目录,然后运行如下命令:

npm i @ngrx/store --save

现在我们已经准备好使用NgRx/Store和Angular了。

3. 创建State

State是一个单独的不可变的数据结构。我们会像如下这样创建state:

export interface AppState {
    articleState: ArticleState;
}
export interface ArticleState {
    articles: Article[];
}

4. 创建Action类

NgRx的Action描述了状态的变化。对于每一个action,我们都需要创建一个继承自Action的类,同时定义其type和payload(payload是个可选参数)。

export const JAVA = 'Java';
export const  MY_ARTICLES = 'Favorite_Articles';
​
export class JavaArticlesAction implements Action {
  readonly type = JAVA;
}
export class FavoriteArticlesAction implements Action {
  readonly type = MY_ARTICLES;
​
  constructor(public payload: Article[]) {}
} 

5. 创建Reducer

Reducer描述了任何一个action所对应的应用的state将怎样变化。reducer创建如下:

export function reducer(state = initialState, action: fromActions.All): ArticleState {
  switch(action.type) {
    case fromActions.JAVA: {
      return {articles: JAVA_ARTICLES};
    }
    case fromActions.ANGULAR: {
      return {articles: ANGULAR_ARTICLES};
    
    } 
    case fromActions.MY_ARTICLES: {
      return {articles: action.payload};
    }    
    default: {
      return state;
    }
  } 
} 

6. 使用createFeatureSelector和createSelector()

createFeatureSelector()被用于为任意指定的state创建一个feature selector。

export const getArticleState = createFeatureSelector<ArticleState>('articleState'); 

createSelector()使用feature selector来创建selector。

export const getArticles = createSelector(
    getArticleState, 
    (state: ArticleState) => state.articles 
); 

7. 使用ActionReducerMap

ActionReducerMap注册了reducer。我们需要在我们配置我们的reducer的地方创建一个 ActionReducerMap的常量。然后在应用模块中使用StoreModule来配置这个常量。

export const reducers: ActionReducerMap<AppState> = {
  articleState: articleReducer.reducer
}; 

8.使用ActionReducer

ActionReducer用来创建类似logger的reducer,使用MetaReducer对它进行配置。

export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
  return function(state: AppState, action: any): AppState {
    console.log('state', state);
    console.log('action', action);
    return reducer(state, action);
  };
} 

通过使用上面的代码,我们可以在控制台中获取每个action的状态和名称。

9.使用MetaReducer

MetaReducer由我们所创建的一系列ActionReducer所组成。在应用中使用StoreModule配置的MetaReducer构成了根meta-reducer。@ngrx/store默认使用 combineReducers在创建根meta-reducer。

export const metaReducers: MetaReducer<AppState>[] = !environment.production
  ? [logger]
  : []; 

10. 使用StoreModule

StoreModule@ngrx/storeAPI中的一个模块,它被用来在应用模块中配置reducer。

@NgModule({
  imports: [     
        ---
    StoreModule.forRoot(reducers, {metaReducers})
  ]
  ---
}) 

11. 使用Store.select()Store.dispatch()

Store.select()Store.dispatch()与是reducer配合使用的。Store.select()用于选择一个selector,Store.dispatch()用于向reducer分发action的类型。 要使用Store, 在组件中创建一个Observable类型的属性。

articles: Observable<Article[]> 

现在使用依赖注入来实例化Store,然后选择一个selector。

constructor(private store: Store<ArticleState>) {
    this.articles = store.select(articleReducer.getArticles);
} 

现在分发这个action以通过reducer改变state。

showJavaArticles(){
    this.store.dispatch(new fromActions.JavaArticlesAction());
} 

12. 完整的例子

下面是我们的例子的项目结构:

my-app
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--actions
|   |   |    |
|   |   |    |--article.actions.ts
|   |   |
|   |   |--components
|   |   |    |
|   |   |    |--article.component.html
|   |   |    |--article.component.ts
|   |   |
|   |   |--models
|   |   |    |
|   |   |    |--article.ts
|   |   |
|   |   |--reducers
|   |   |    |
|   |   |    |--app.states.ts
|   |   |    |--article.reducer.ts
|   |   |    |--reducers.ts
|   |   |
|   |   |--app.component.ts
|   |   |--app.module.ts 
|   | 
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 

以下是完整的代码: article.actions.ts

import { Action } from '@ngrx/store';
import { Article } from '../models/article';
​
export const JAVA = 'Java';
export const  ANGULAR = 'Angular';
export const  MY_ARTICLES = 'Favorite_Articles';
​
export class JavaArticlesAction implements Action {
  readonly type = JAVA;
}
​
export class AngularArticlesAction implements Action {
  readonly type = ANGULAR;
}
​
export class FavoriteArticlesAction implements Action {
  readonly type = MY_ARTICLES;
​
  constructor(public payload: Article[]) {}
}
​
export type All = JavaArticlesAction | AngularArticlesAction | FavoriteArticlesAction; 

article.ts

export interface Article {
    id: number;
    title: string;
    category: string;
}
​
export const JAVA_ARTICLES: Article[] = [
    {id: 1, title: 'Java Article 1', category: 'Java'},
    {id: 2, title: 'Java Article 2', category: 'Java'},
]
export const ANGULAR_ARTICLES: Article[] = [
    {id: 1, title: 'Angular Article 1', category: 'Angular'},
    {id: 2, title: 'Angular Article 2', category: 'Angular'},
]
export const FAVORITE_ARTICLES: Article[] = [
    {id: 1, title: 'Java Article 1', category: 'Java'},
    {id: 2, title: 'Angular Article 2', category: 'Angular'}
] 

app.states.ts

import { Article } from '../models/article';
​
export interface AppState {
    articleState: ArticleState;
}
​
export interface ArticleState {
    articles: Article[];
} 

article.reducer.ts

import { createFeatureSelector, createSelector } from '@ngrx/store';
import * as fromActions from '../actions/article.actions';
import { JAVA_ARTICLES, ANGULAR_ARTICLES } from '../models/article';
import { ArticleState } from './app.states';
​
export const initialState: ArticleState = { articles: []};
​
export function reducer(state = initialState, action: fromActions.All): ArticleState {
  switch(action.type) {
    case fromActions.JAVA: {
      return {articles: JAVA_ARTICLES};
    }
    case fromActions.ANGULAR: {
      return {articles: ANGULAR_ARTICLES};
    
    } 
    case fromActions.MY_ARTICLES: {
      return {articles: action.payload};
    }    
    default: {
      return state;
    }
  } 
}
​
export const getArticleState = createFeatureSelector<ArticleState>('articleState');
​
export const getArticles = createSelector(
    getArticleState, 
    (state: ArticleState) => state.articles 
); 

reducer.ts

import { ActionReducerMap, ActionReducer, MetaReducer } from '@ngrx/store';
import { AppState } from './app.states';
import * as articleReducer from './article.reducer';
import { environment } from '../../environments/environment';
​
export const reducers: ActionReducerMap<AppState> = {
  articleState: articleReducer.reducer
};
​
export function logger(reducer: ActionReducer<AppState>): ActionReducer<AppState> {
  return function(state: AppState, action: any): AppState {
    console.log('state', state);
    console.log('action', action);
    return reducer(state, action);
  };
}
​
export const metaReducers: MetaReducer<AppState>[] = !environment.production
  ? [logger]
  : [];

article.component.ts

import { Store } from '@ngrx/store';
import { Component } from '@angular/core';    
import { Observable } from 'rxjs/Observable';
import * as articleReducer from '../reducers/article.reducer';
import * as fromActions from '../actions/article.actions';
import { ArticleState } from '../reducers/app.states';
import { Article, FAVORITE_ARTICLES } from '../models/article';
​
@Component({
    selector: 'app-article',
    templateUrl: 'article.component.html'
})
export class ArticleComponent {
    articles: Observable<Article[]>
​
    constructor(private store: Store<ArticleState>) {
        this.articles = store.select(articleReducer.getArticles);
    }
    showJavaArticles(){
        this.store.dispatch(new fromActions.JavaArticlesAction());
    }
    showAngularArticles(){
        this.store.dispatch(new fromActions.AngularArticlesAction());
    }
    showFavoriteArticles(){
        this.store.dispatch(new fromActions.FavoriteArticlesAction(FAVORITE_ARTICLES));
    }
} 

article.component.html

<button (click)="showJavaArticles()">Java Articles</button>
<button (click)="showAngularArticles()">Angular Articles</button>
<button (click)="showFavoriteArticles()">Favorite Articles</button>
<ul>
   <li *ngFor="let article of articles | async">
        {{article.id}} - {{article.title}} - {{article.category}}   
   </li>
</ul> 

app.component.ts

import { Component } from '@angular/core';
​
@Component({
   selector: 'app-root',
   template: `
    <app-article></app-article>
     `
})
export class AppComponent {
} 

app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { StoreModule } from '@ngrx/store';
import { AppComponent }  from './app.component';
import { ArticleComponent }  from './components/article.component';
import { reducers, metaReducers } from './reducers/reducers';
​
@NgModule({
  imports: [     
        BrowserModule,
    StoreModule.forRoot(reducers, {metaReducers})
  ],
  declarations: [
        AppComponent,
    ArticleComponent
  ],
  providers: [
​
  ],
  bootstrap: [
        AppComponent
  ]
})
export class AppModule { } 

作者:neromaycry

链接:https://www.jianshu.com/p/c2d61fc76128

來源:简书

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值