ngrx/store

634 阅读5分钟

1、ngrx/store是什么

ngrx/store是Angular的状态管理库,基于Redux模式构建,且它必须和rxjs一起使用,因为它使用rxjs管理状态,并且可以使用rxjs的操作符和工具来处理状态数据流。

2、主要文件构成

  1. state.ts

    1. 使用interface、enum、class定义所有状态
  2. action.ts

    1. 使用createAction(p1,p2?)方法创建action,action本质是一个对象,所有状态的更改都需要通过派发(dispatch)action的形式。
    2. p1是一个字符串,用来定义action的type,必须使用字符串来标识action,避免手写字符串时发生错误,也方便未来的维护和修改。
    3. p2是一个函数,接收一个对象参数。
  3. reducer.ts

    1. 使用createReducer(initState,...ons)方法创建reducer,用于监听action,更新state。
    2. initState,一个对象,指定了store的初始状态。
    3. on方法是action的处理函数,每个action对应一个处理函数。
  4. effect.ts

    1. 用于发起请求
  5. ...module.ts

    1. 定义完store后需要在模块中进行注册,如果是多个store的话,可以在app.module.ts中进行全局注册,如果是单个store,可以只在对应模块下注册。
    2. 单个模块注册:
      1. 使用StoreModule.forFeature(featureName: string, reducers: ActionReducerMap<any>, config?: StoreConfig<any>)方法,它的作用是将指定的Feature模块的Store注册到应用的Store中。
      2. featureName: string 是 Feature Module 的名称,在 Store 的状态树上会以该名称作为键值,使用多个 Feature Module 时,它可以让我们快速找到对应的状态树。
      3. 第二个参数可以是一个包含一系列reducer的对象,也可以是一个InjectionToken。如果使用的是InjectionToken,它应该指向一个ActionReducerMap对象,它包括被注册的Feature模块的所有reducers。ActionReducerMap对象可以由工厂函数生成。
      imports: [StoreModule.forFeature(USER_MANAGE, USER_MANAGE_TOKEN)]
      providers: [
          {
            provide: USER_MANAGE_TOKEN,
            useFactory: getUserManageReducers, 
            //useFactory(getUserManageReducers)则是一个工厂函数,用于生成一个特定的类型实例。
            //在这里,它用于生成一个 ActionReducerMap 类型的实例,这个实例包含了一组指定的 reducer。
            //也就是说,在依赖注入时会调用 getUserManageReducers 方法,生成一个指定的对象,
            //并将该对象作为 USER_MANAGE_TOKEN 的依赖项值传递给应用中的所有组件。
          }
        ]
      
        //getUserManageReducers函数如下 
        export function getUserManageReducers() {
            return {
              [UserManageName.USER_ANALYSIS]: UserAnalysisReducer.userAnalysisReducer
            };
          }
        //其中UserManageName.USER_ANALYSIS是定义的一个常量。
        //UserAnalysisReducer.userAnalysisReducer是一个由createReducer方法创建的reducer。
      
    3. 全局注册:
      1. 使用StoreModule.forRoot(reducers,config?)方法,
      2. reducers: ActionReducerMap<any>参数是一个对象。这个参数的值是一个 ActionReducerMap 类型的变量,它定义了一个对象,该对象的每个属性都对应一个 reducer 函数。
    4. config?: StoreConfig<any>,该参数是一个可选参数,用于配置 Store。它包含以下几个属性:initialState 表示 Store 的初始状态,默认为 {}runtimeChecks 表示运行时检查的数组,用于配置要执行哪些运行时检查,默认为 {}
  6. 使用store

    1. 首先要使用select方法获取状态源,这个状态源对应的就是我们在模块中注册的reducer。
    2. 可以使用链式的方式获取某个状态,比如
    this.store.pipe(
      select(UserManageName.USER_MANAGE),
      //select的参数要和module.ts文件中的StoreModule.forFeature函数的第一个参数一致。
      //查询到的其实是一个reducer。即用createReducer创建的。
      //换句话说,我们的store其实就是一个reducer。
      //此处我们查到UserManageName.USER_MANAGE对应了一个USER_MANAGE_TOKEN,
      //而USER_MANAGE对应了一个USER_MANAGE_TOKEN是由getUserManageReducers函数生成的reducer集合。
      //所以在此处的第一个查询,查到的就是getUserManageReducers函数的返回值。即{user_analysis: {…}}。
      select(UserManageName.USER_ANALYSIS),//查询到的是{user_analysis: {…}}中的user_analysis对象
      select('userAnalysisListRequest'),//user_analysis对象中的一个属性
      select('pageSize')//userAnalysisListRequest下的一个属性
    )
    
    
     //如果不想每次都先查store源,再查下面的属性。
     //可以统一查询一次源store。以后的每个属性,在这个的基础上再继续查即可。
     cstHomeListStore: Observable<cstCustomerHomeListState.CstCustomerHomeListState> =
       this.store.pipe(select(cstCustomerManage.cstHomeList));//在此处查询一次store源。
      
      //在下面2个地方直接使用this.cstHomeListStore.pipe即可。
      //effectivePeriod
       effectivePeriod: Observable<number> =
       this.cstHomeListStore.pipe(
         select(cstCustomerHomeListState.cstCustomerHomeListStateEnum.listRequest),
         select(cstCustomerHomeListState.listRequestEnum.effective),
       );
       //enterpeiseInfo
       enterpeiseInfo: Observable<number> =
       this.cstHomeListStore.pipe(
         select(cstCustomerHomeListState.cstCustomerHomeListStateEnum.listRequest),
         select(cstCustomerHomeListState.listRequestEnum.companyInfo),
       );
    

    下面是3个项目中实际用到的state、action、reducer的文件,供学习。

    //state.ts
        /**学员增长分析列表 */
    export enum userAnalysisListEnum {
      date = 'date',
      registerUser = 'registerUser',
      totalUser = 'totalUser',//累计学员数
      pcWeb = 'pcWeb',//PC网页
      wechatWeb = 'wechatWeb', //微信网页
      mobileH5 = 'mobileH5', //手机H5
      app = 'app',//APP
      miniProgram = 'miniProgram',//小程序
      agentChannel = 'agentChannel',//代理渠道
      softwareUser = 'softwareUser',  //软件用户
    }
    
    export interface UserAnalysisList {
      [userAnalysisListEnum.date]:string;
      [userAnalysisListEnum.registerUser]:number;
      [userAnalysisListEnum.agentChannel]:number;
      [userAnalysisListEnum.app]:number;
      [userAnalysisListEnum.mobileH5]:number;
      [userAnalysisListEnum.miniProgram]:number;
      [userAnalysisListEnum.pcWeb]:number;
      [userAnalysisListEnum.softwareUser]:number;
      [userAnalysisListEnum.totalUser]:number;
      [userAnalysisListEnum.wechatWeb]:number;
    }
    
    /** 新增用户request */
    export class UserAnalysisListRequest {
      pageIndex = 1;
      pageSize = 30;
      timeFrom: number;
      timeTo: number;
    }
    
    /**
     * 接口返回
     */
    export interface Result<T> {
      code: number;
      data: T;
      detailMsg: string;
      msg: string;
      succeed: boolean;
    }
    
    export interface ResultData<T> {
      count: number;
      data: any;
      dataAmount: number;
      index: number;
      pageAmount: number;
      rows: T;
    }
    
    /**
     * 总store
     */
    export interface UserAnalysisState {
      tabIndex: number;
      userAnalysisListRequest: UserAnalysisListRequest;
      tableLoading:boolean;
      userAnalysisList: UserAnalysisList[];
    }
    
    
    //action.ts
    import {createAction, props} from '@ngrx/store';
    import * as UserAnalysisState from './state';
    
    
    
    
    export const CHANGETABINDEX = '[user manage] [user analysis] change tabindex';
    
    
    
    /**
     * 改变用户列表
     */
    export const CHANGEUSERLIST = '[user manage] [user analysis] change  user list';
    
    
    /**
     * 改变totalPage
     */
    export const CHANGETOTALPAGE = '[user manage] [user analysis] change  total page';
    
    /**
     * 改变pageIndex
     */
    export const CHANGEPAGEINDEX = '[user manage] [user analysis] change  page index';
    
    /**
     * 改变pageSize
     */
    export const CHANGEPAGESIZE = '[user manage] [user analysis] change  page size';
    
    /**
     * 改timeFrom
     */
    export const CHANGETIMEFROM = '[user manage] [user analysis] change  time from';
    
    
    /**
     * 改变timeTo
     */
    export const CHANGETIMETO = '[user manage] [user analysis] change  time to';
    
    
    /**
     * 改变列表loading
     */
    export const CHANGETABLELOADING = '[user manage] [user analysis] change table loading';
    
    /**
     * 修改时间
     */
    export const CHANGERANGEDATE = '[user manage] [user analysis] change range date';
    
    
    export const changeTabIndex = createAction(CHANGETABINDEX, props<{tabIndex: number}>());
    export const changeUserList = createAction(CHANGEUSERLIST,props<{list: UserAnalysisState.UserAnalysisList[]}>());
    export const changeTotalPage = createAction(CHANGETOTALPAGE, props<{totalPage: number}>());
    export const changePageIndex = createAction(CHANGEPAGEINDEX, props<{pageIndex: number}>());
    export const changePageSize = createAction(CHANGEPAGESIZE, props<{pageSize: number}>());
    export const changeTimeFrom = createAction(CHANGETIMEFROM, props<{timeFrom: number}>());
    export const changeTimeTo = createAction(CHANGETIMETO, props<{timeTo: number}>());
    export const changeTableLoading = createAction(CHANGETABLELOADING, props<{isLoading: boolean}>());
    export const changeRangeDate = createAction(CHANGERANGEDATE,props<{rangeDate:Date|null[]}>())
    
    
    //reducer.ts
    import * as UserAnalysisAction from './action';
    import * as UserAnalysisState from './state';
    import {Action, ActionReducer, createReducer, on} from '@ngrx/store';
    
    const newUserRequest: UserAnalysisState.UserAnalysisListRequest = 
    new UserAnalysisState.UserAnalysisListRequest();
    
    const userAnalysisInitState: UserAnalysisState.UserAnalysisState = {
      userAnalysisListRequest: {...newUserRequest},
      tableLoading: false,
      userAnalysisList: [],
      tabIndex: 0,
    };
    
    
    const reducer: ActionReducer<UserAnalysisState.UserAnalysisState | undefined, Action> =
      createReducer(
        userAnalysisInitState,
        on(UserAnalysisAction.changeTabIndex,
          (state, action): UserAnalysisState.UserAnalysisState => 
          ({...state, tabIndex: action.tabIndex})),
        on(UserAnalysisAction.changePageIndex,
          (state, action): UserAnalysisState.UserAnalysisState => 
          ( {...state, userAnalysisListRequest: 
              {...state.userAnalysisListRequest, pageIndex: action.pageIndex}
            }
          )),
      );
    
    export function userAnalysisReducer(state: UserAnalysisState.UserAnalysisState | undefined, action: Action)
      : UserAnalysisState.UserAnalysisState {
      return reducer(state, action);
    }
    
    
    //componnet.ts
    import { Component, OnInit } from '@angular/core';
    import {select, Store} from '@ngrx/store';
    import {UserManageState} from '../store/reg-token';
    import {Observable} from 'rxjs';
    import * as UserManageName from '../store';
    import * as UserAnalysisState from '../store/user-analysis/new/state';
    import * as UserAnalysisAction from '../store/user-analysis/new/action';
    import {PageOptions} from '../../../entity/pollen';
    import {ActivatedRoute,Router} from '@angular/router'
    @Component({
      selector: 'app-user-analysis',
      templateUrl: './user-analysis.component.html',
      styleUrls: ['./user-analysis.component.scss']
    })
    export class UserAnalysisComponent implements OnInit {
      dateRange = [new Date().setDate(1), new Date()];
      selectTime=0;
      pageSizeOptions = PageOptions;
    
      tabIndex: Observable<number> =
        this.store.pipe(
          select(UserManageName.USER_MANAGE), select(UserManageName.USER_ANALYSIS), select('tabIndex')
        );//tabIndex被定义为一个可观察对象
        //UserManageName.USER_MANAGE模块是在reg-token里面定义的
      tableLoading:Observable<boolean>=
        this.store.pipe(
          select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('tableLoading')
        );
      request:Observable<any>=
        this.store.pipe(
          select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('userAnalysisListRequest')
        );
      pageIndex:Observable<number>=
        this.store.pipe(
          select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('userAnalysisListRequest'),
          select('pageIndex')
        );
    
      tableList:Observable<Array<UserAnalysisState.UserAnalysisList>>=
        this.store.pipe(
          select(UserManageName.USER_MANAGE),select(UserManageName.USER_ANALYSIS),select('tableList')
        )
      pageSize:Observable<number>=
        this.store.pipe(
          select(UserManageName.USER_MANAGE),//select的参数要和module.ts文件中的StoreModule.forFeature函数的第一个参数一致。
          //查询到的其实是一个reducer。即用createReducer创建的。换句话说,我们的store其实就是一个reducer。
          //在这里查询到的是一个对象:{user_analysis: {…}}。这个对象是getUserManageReducers函数的返回值。
          select(UserManageName.USER_ANALYSIS),//查询到的是reducer函数返回的state对象
          select('userAnalysisListRequest'),
          select('pageSize')
        )
      constructor(
        private store: Store<any>
      ) {
       }
    
      ngOnInit() {
        this.store.pipe(
          select(UserManageName.USER_MANAGE),
        ).subscribe(res=>{
          console.log(res)
        })
        this.pageIndex.subscribe(res=>{
          console.log('pageIndex',res)
        })
        this.pageSize.subscribe(res=>{
          console.log('pageSize',res)
        })
      }
    
      changeTabIndex(tabIndex: number): void {
        this.selectTime = tabIndex == 0 ? 0 :5;
        this.store.dispatch(UserAnalysisAction.changeTabIndex({tabIndex}));
      }
    }