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之间耦合
注意事项
- 使用ngrx,需要考虑到对象暂存内存中,譬如离开页面,需要销毁,否则再次进入设置的是旧值;
- angular中code执行时机的问题,先后顺序不同,出现下面可能问题:
- 有时候code修改了,但是界面没有修改;
- 动态设置div id="{{}}",但是使用d3.select("#{id}")始终获取不到,需要setTimeout才行;可以改用class,非动态id。
简单入门
初始化项目
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#scss ]
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.
创建Angular组件my-counter
ng generate @schematics/angular:component my-counter --style=scss <
安装ngrx和配套cli指令扩展
npm i @ngrx/store @ngrx/effects @ngrx/store-devtools @ngrx/schematics
尝试创建state,发生unhandled异常
ng generate store AppState --root --module app.module.ts
更新cli指令集
ng config cli.defaultCollection @ngrx/schematics
再次更新
reducers已生成
import {
ActionReducer,
ActionReducerMap,
createFeatureSelector,
createSelector,
MetaReducer
} from '@ngrx/store';
import { environment } from '../../environments/environment';
export interface State {
}
export const reducers: ActionReducerMap<State> = {
};
export const metaReducers: MetaReducer<State>[] = !environment.production ? [] : [];
生成action
ng generate action actions/counter
# app/actions/counter.actions.ts
import { createAction, props } from '@ngrx/store';
export const loadCounters = createAction(
'[Counter] Load Counters'
);
export const loadCountersSuccess = createAction(
'[Counter] Load Counters Success',
props<{ data: any }>()
);
export const loadCountersFailure = createAction(
'[Counter] Load Counters Failure',
props<{ error: any }>()
);
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 {}
定义组件
# app / my-counter / my-counter.component.html
<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
# app / my-counter / my-counter.component.ts
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 }>) {
this.count$ = store.pipe(select('count'));
}
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写法解惑
# 查看store源码,我们得到Observable
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为对象时
# counter.reducer.ts
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
# pre.reducer.ts
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相关的函数
# Selector 产生V类型结果
export declare type Selector<T, V> = (state: T) => V;
SelectorWithProps源码
# 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>;
总结
- createSelector(): 入参state函数,返回selector纯函数;形式为state函数;可以嵌套
- createFeatureSelector:入参state函数,返回selector纯函数;形式为state函数;可以嵌套
- selector():入参state函数,也可入参selector纯函数;返回Observable可观察对象
selector实践
定义state对象
# counter.selector.ts
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() {
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 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
ng generate effect effects/hello --root --module app.module.ts
import { Injectable } from '@angular/core';
import { Actions, createEffect } from '@ngrx/effects';
@Injectable()
export class HelloEffects {
constructor(private actions$: Actions) {}
}
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() {
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() {
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);
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) {
return localCounterReducer(state, action);
};
更多推荐
参考文献: