ngrx概念
- 一个框架
- 用于构建Angular8响应式应用
- 用于状态管理
- 使用可观察对象
- 使用Typescript
- 使用OnPush策略,变动检测更高效
- 状态序列化存储
- 易于测试
原理图
- component触发特定action(事件)
- store中存储着state、reducer。state对象存放着感兴趣的字段
- action能够触发effect获取业务数据,effect处理完毕,再次触发action进行通知
- action也能够直接触发reducer更新state
- reducer产生新的state
- selector层层筛选出state中感兴趣的那部分字段
- state 感兴趣的字段修改了,component对应的视图进行响应式变化
- 综上完成解耦,一个轮回完成视图状态修改
什么时候使用ngrx
组件之间共享的state管理
解耦组件与service之间耦合
简单入门
初始化项目
ng new ngrx-demo
复制代码
> ng new ngrx-demo
? Would you like to add Angular routing? Yes
? Which stylesheet format would you like to use? SCSS [ https://sass-lang.com/
documentation/syntax
CREATE ngrx-demo/README.md (1026 bytes)
CREATE ngrx-demo/.editorconfig (246 bytes)
CREATE ngrx-demo/.gitignore (631 bytes)
CREATE ngrx-demo/angular.json (3705 bytes)
CREATE ngrx-demo/package.json (1295 bytes)
CREATE ngrx-demo/tsconfig.json (543 bytes)
CREATE ngrx-demo/tslint.json (1953 bytes)
CREATE ngrx-demo/browserslist (429 bytes)
CREATE ngrx-demo/karma.conf.js (1021 bytes)
CREATE ngrx-demo/tsconfig.app.json (270 bytes)
CREATE ngrx-demo/tsconfig.spec.json (270 bytes)
CREATE ngrx-demo/src/favicon.ico (948 bytes)
CREATE ngrx-demo/src/index.html (294 bytes)
CREATE ngrx-demo/src/main.ts (372 bytes)
....
added 1461 packages from 1071 contributors in 85.831s
Successfully initialized git.
复制代码
安装ngrx
npm install @ngrx/store --save
复制代码
建立Angular组件my-counter
ng generate @schematics/angular:component my-counter --style=scss <
复制代码
定义action
import { createAction } from '@ngrx/store';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
复制代码
定义reducer初始状态为数值
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export const initialState = 0;
const _counterReducer = createReducer(initialState,
on(increment, state => state + 1),
on(decrement, state => state - 1),
on(reset, state => 0),
);
export function counterReducer(state, action) {
return _counterReducer(state, action);
}
复制代码
注册state
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
declarations: [AppComponent],
imports: [
BrowserModule,
StoreModule.forRoot({ count: counterReducer }) // 注册时指定key
],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
复制代码
定义组件
复制代码
<button id="increment" (click)="increment()">Increment</button>
<div>Current Count: {{ count$ | async }}</div>
<button id="decrement" (click)="decrement()">Decrement</button>
<button id="reset" (click)="reset()">Reset Counter</button>
复制代码
组件触发action
复制代码
import { Component } from '@angular/core';
import { Store, select } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from '../counter.actions';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.css'],
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{ count: number }>) { //这里须要key-count保持一致,
this.count$ = store.pipe(select('count')); //这里关键点,在于app.module.ts注册时候的key
}
increment() {
console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
复制代码
组件生效
<div>
this is app component
<app-my-counter></app-my-counter>
</div>
复制代码
store.pipe写法解惑
export declare class Store<T> extends Observable<T> implements Observer<Action> {
....
}
复制代码
store.select也是Observable
export declare function select<T, Props, K>(mapFn: (state: T, props: Props) => K, props?: Props): (source$: Observable<T>) => Observable<K>;
复制代码
this.store
.select(fromStore.getProductsState)
.map(state => state.pizzas)
.map(pizzas => pizza.entities);
复制代码
进阶
当reducer初始状态state为对象时
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset } from './counter.actions';
export interface State {
away: number;
}
export const initialState: State = {
away: 0,
};
const _counterReducer = createReducer(initialState,
on(increment, (state) => {
console.log('reducer increment....');
return {...state, away: state.away + 1};
}),
on(decrement, state => ({...state, away: state.away - 1})),
on(reset, state => ({...state, away: 0})),
);
export function counterReducer(state, action) {
return _counterReducer(state, action);
}
复制代码
延迟加载state注册
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
@NgModule({
imports: [
StoreModule.forFeature('countFeture', counterReducer)
],
})
export class ScoreboardModule {}
复制代码
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}),
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
复制代码
root中配置前置reducer
import { ActionReducer, MetaReducer } from '@ngrx/store';
export function debug(reducer: ActionReducer<any>): ActionReducer<any> {
return function( state, action) {
console.log('pre state', state);
console.log('pre action', action);
return reducer(state, action);
};
}
export const metaReducers: MetaReducer<any>[] = [debug];
复制代码
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
import { metaReducers } from './pre.reducer';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}, { metaReducers }), //前置reducer
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
复制代码
counter increment...
pre state {countFeture: {…}}
pre action {type: "[Counter Component] Increment"}
reducer increment....
复制代码
feature中配置前置reducer
import { NgModule } from '@angular/core';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { metaReducers } from './pre.reducer';
@NgModule({
imports: [
StoreModule.forFeature('countFeture', counterReducer , { metaReducers })
],
})
export class ScoreboardModule {}
复制代码
counter increment...
pre state {countFeture: {…}}
pre action {type: "[Counter Component] Increment"}
reducer increment....
复制代码
selector解读
Selector 源码 -- state相关的函数
export declare type Selector<T, V> = (state: T) => V;
复制代码
SelectorWithProps源码
export declare type SelectorWithProps<State, Props, Result> = (state: State, props: Props) => Result;
复制代码
MemoizedSelector是Selector子集
export interface MemoizedSelector<State, Result, ProjectorFn = DefaultProjectorFn<Result>> extends Selector<State, Result> {
release(): void;
projector: ProjectorFn;
setResult: (result?: Result) => void;
}
export interface MemoizedSelectorWithProps<State, Props, Result, ProjectorFn = DefaultProjectorFn<Result>> extends SelectorWithProps<State, Props, Result> {
release(): void;
projector: ProjectorFn;
setResult: (result?: Result) => void;
}
复制代码
createFeatureSelector源码
export declare function createFeatureSelector<T>(featureName: string): MemoizedSelector<object, T>;
export declare function createFeatureSelector<T, V>(featureName: keyof T): MemoizedSelector<T, V>;
复制代码
createSelector源码
export declare function createSelector<State, S1, Result>(s1: Selector<State, S1>, projector: (s1: S1) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, Result>(s1: SelectorWithProps<State, Props, S1>, projector: (s1: S1, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, Result>(selectors: [Selector<State, S1>], projector: (s1: S1) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, Result>(selectors: [SelectorWithProps<State, Props, S1>], projector: (s1: S1, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, projector: (s1: S1, s2: S2) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, projector: (s1: S1, s2: S2, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, Result>(selectors: [Selector<State, S1>, Selector<State, S2>], projector: (s1: S1, s2: S2) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>], projector: (s1: S1, s2: S2, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, projector: (s1: S1, s2: S2, s3: S3) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>], projector: (s1: S1, s2: S2, s3: S3) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>], projector: (s1: S1, s2: S2, s3: S3, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>], projector: (s1: S1, s2: S2, s3: S3, s4: S4) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, s7: Selector<State, S7>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, s7: SelectorWithProps<State, Props, S7>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>, Selector<State, S7>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>, SelectorWithProps<State, Props, S7>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(s1: Selector<State, S1>, s2: Selector<State, S2>, s3: Selector<State, S3>, s4: Selector<State, S4>, s5: Selector<State, S5>, s6: Selector<State, S6>, s7: Selector<State, S7>, s8: Selector<State, S8>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, S8, Result>(s1: SelectorWithProps<State, Props, S1>, s2: SelectorWithProps<State, Props, S2>, s3: SelectorWithProps<State, Props, S3>, s4: SelectorWithProps<State, Props, S4>, s5: SelectorWithProps<State, Props, S5>, s6: SelectorWithProps<State, Props, S6>, s7: SelectorWithProps<State, Props, S7>, s8: SelectorWithProps<State, Props, S8>, projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
export declare function createSelector<State, S1, S2, S3, S4, S5, S6, S7, S8, Result>(selectors: [Selector<State, S1>, Selector<State, S2>, Selector<State, S3>, Selector<State, S4>, Selector<State, S5>, Selector<State, S6>, Selector<State, S7>, Selector<State, S8>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8) => Result): MemoizedSelector<State, Result>;
export declare function createSelector<State, Props, S1, S2, S3, S4, S5, S6, S7, S8, Result>(selectors: [SelectorWithProps<State, Props, S1>, SelectorWithProps<State, Props, S2>, SelectorWithProps<State, Props, S3>, SelectorWithProps<State, Props, S4>, SelectorWithProps<State, Props, S5>, SelectorWithProps<State, Props, S6>, SelectorWithProps<State, Props, S7>, SelectorWithProps<State, Props, S8>], projector: (s1: S1, s2: S2, s3: S3, s4: S4, s5: S5, s6: S6, s7: S7, s8: S8, props: Props) => Result): MemoizedSelectorWithProps<State, Props, Result>;
e
复制代码
select源码
export declare function select<T, Props, K>(mapFn: (state: T, props: Props) => K, props?: Props): (source$: Observable<T>) => Observable<K>;
//其中mapFn: (state: T, props: Props) => K 就是SelectorWithProps
复制代码
总结
- createSelector(): 入参state函数,返回selector纯函数;形式为state函数;能够嵌套
- createFeatureSelector:入参state函数,返回selector纯函数;形式为state函数;能够嵌套
- selector():入参state函数,也可入参selector纯函数;返回Observable可观察对象
selector实践
定义state对象
export interface State {
count: number;
}
export interface AppState {
feature: State;
}
复制代码
feature中注册
@NgModule({
imports: [
StoreModule.forFeature('feature', counterReducer )
],
})
export class ScoreboardModule {}
复制代码
定义关心的state字段函数
export const countFeatureFn = (state: AppState, props) => {
console.log('feature....', state);
return state.feature;
};
export const countStateFn = (state: State, props) => {
console.log('state....', state);
return state.count * props.multiply;
};
复制代码
建立selector函数
export const countStateSelector = createSelector(countFeatureFn, countStateFn);
复制代码
经过select(),创建与store的关联,订阅数据
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.scss'],
})
export class MyCounterComponent {
count$: Observable<number>;
constructor(private store: Store<{feature: State }>) {
this.count$ = store.pipe(select(countStateSelector, { multiply: 3}));
}
....
}
复制代码
触发store中state改变,刷新component便可
increment() {
// console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
复制代码
引入常量,优化
export const featureKey = 'feature';
复制代码
使用createFeatureSelector
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { featureKey } from './feature.enum';
export interface State {
count: number;
}
export interface AppState {
[featureKey]: State;
}
// export const countFeatureFn = (state: AppState, props) => {
// console.log('feature....', state);
// return state[featureKey];
// };
export const countStateFn = (state: State, props) => {
console.log('state....', state);
return state.count * props.multiply;
};
export const selectFeature = createFeatureSelector<AppState, State>(featureKey);
export const countStateSelector = createSelector(selectFeature, countStateFn);
复制代码
effects 实践
effects用途
- 监听特定动做,调用业务逻辑。譬如登陆逻辑。
- 特定逻辑调用完毕后,发射特定的动做,触发reducer,修改状态,更新组件。
添加effects
import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { EMPTY } from 'rxjs';
import { map, mergeMap, catchError } from 'rxjs/operators';
import { HelloService } from './hello.service';
import { increment, decrement, reset, hello, helloSuccess } from './counter.actions';
@Injectable()
export class HelloEffects {
loadMovies$ = createEffect(() => this.actions$.pipe(
ofType(hello),
mergeMap((action) => this.helloService.getHello(action)
.pipe(
map((movies: string) => {
console.log('in effect map...', movies);
return helloSuccess({title: movies});
}),
catchError(() => EMPTY)
)
)
)
);
constructor(
private actions$: Actions,
private helloService: HelloService
) {}
}
复制代码
root中注册effects
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { StoreModule } from '@ngrx/store';
import { counterReducer } from './counter.reducer';
import { MyCounterComponent } from './my-counter/my-counter.component';
import { ScoreboardModule } from './feature.module';
import { metaReducers } from './pre.reducer';
import { EffectsModule } from '@ngrx/effects';
import { HelloEffects } from './hello.effects';
@NgModule({
declarations: [
AppComponent,
MyCounterComponent
],
imports: [
BrowserModule,
AppRoutingModule,
StoreModule.forRoot({}),
EffectsModule.forRoot([HelloEffects]),
ScoreboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
复制代码
添加service
import { Injectable } from '@angular/core';
import { of } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class HelloService {
constructor() { }
getHello() {
// console.log('service get hello....');
return of('hello world ngrx');
}
}
复制代码
action修改
import { createAction, props } from '@ngrx/store';
import { State, TitleState } from './counter.selector';
export const increment = createAction('[Counter Component] Increment');
export const decrement = createAction('[Counter Component] Decrement');
export const reset = createAction('[Counter Component] Reset');
export const hello = createAction('[Hello Service] call', props<TitleState>());
export const helloSuccess = createAction('[Hello Service] call success', props<TitleState>());
复制代码
组件中触发effect定义的动做
import { Component, OnInit } from '@angular/core';
import { Store, select, createSelector, createFeatureSelector } from '@ngrx/store';
import { Observable } from 'rxjs';
import { increment, decrement, reset } from '../counter.actions';
import { countStateSelector, titleStateSelector, State, AppState } from '../counter.selector';
import { HelloService } from '../hello.service';
import { hello } from '../counter.actions';
@Component({
selector: 'app-my-counter',
templateUrl: './my-counter.component.html',
styleUrls: ['./my-counter.component.scss'],
})
export class MyCounterComponent implements OnInit{
count$: Observable<number>;
title$: Observable<string>;
constructor(private store: Store<AppState>) {
this.count$ = store.pipe(select(countStateSelector, { multiply: 2}));
this.title$ = store.pipe(select(titleStateSelector));
}
ngOnInit(): void {
this.store.dispatch(hello({title: 'hello dispatch', name: 'ngrx '}));
}
increment() {
// console.log('counter increment...');
this.store.dispatch(increment());
}
decrement() {
this.store.dispatch(decrement());
}
reset() {
this.store.dispatch(reset());
}
}
复制代码
selectors筛选特定数据
import { createSelector, createFeatureSelector } from '@ngrx/store';
import { featureKey } from './feature.enum';
export interface AppState {
[featureKey]: State;
}
export interface State {
count: number;
}
export interface TitleState {
title: string;
name?: string;
}
export const countStateFn = (state: State, props) => {
console.log('count state....', state);
return state.count * props.multiply;
};
export const titleStateFn = (state: TitleState) => {
console.log('title state....', state);
return state.title;
};
export const countFeature = createFeatureSelector<AppState, State>(featureKey);
export const titleFeature = createFeatureSelector<AppState, TitleState>(featureKey);
// logic selector
export const countStateSelector = createSelector(countFeature, countStateFn);
export const titleStateSelector = createSelector(titleFeature, titleStateFn);
复制代码
reducer进行数据处理
import { createReducer, on } from '@ngrx/store';
import { increment, decrement, reset, helloSuccess } from './counter.actions';
import { State, TitleState } from './counter.selector';
export const initialState: State & TitleState = {
count: 0,
title: '',
};
const localCounterReducer = createReducer(initialState,
on(increment, (state) => {
console.log('incre....', state);
const res = {...state, count: state.count + 1};
return res;
}),
on(decrement, state => ({...state, count: state.count - 1})),
on(reset, state => ({...state, count: 0})),
on(helloSuccess, (state, { title } ) => {
console.log('param title ....', title);
return { ...state, title } ;
})
);
export function counterReducer(state, action) {
// console.log('counter reducer...', state, action);
return localCounterReducer(state, action);
};
复制代码
参考文献: