zoukankan      html  css  js  c++  java
  • ngRx 官方示例分析

    我们从 Action 名称开始。

    解决 Action 名称冲突问题

    ngRx 中,不同的 Action 需要一个 Action Type 进行区分,一般来说,这个 Action Type 是一个字符串,如何定义和使用这个字符串是需要首先考虑的问题。需要保证不同的 Action 名称不能冲突,使用的时候还需要方便,编码的时候,最好有提示等等。

    首先处理命名冲突问题,示例使用 util 中定义的一个字典来检查是否已经定义了一个 Action

    app/util.ts

    /**
     * This function coerces a string into a string literal type.
     * Using tagged union types in TypeScript 2.0, this enables
     * powerful typechecking of our reducers.
     *
     * Since every action label passes through this function it
     * is a good place to ensure all of our action labels
     * are unique.
     */
    
    const typeCache: { [label: string]: boolean } = {};
    export function type<T>(label: T | ''): T {
      if (typeCache[<string>label]) {
        throw new Error(`Action type "${label}" is not unique"`);
      }
    
      typeCache[<string>label] = true;
    
      return <T>label;
    }

    使用 TypeScript 的 Playground 翻译一下,可以得到如下的结果:

    var typeCache = {};
    function type(label) {
        if (typeCache[label]) {
             throw new Error("Action type "" + label + "" is not unique"");
        }
        typeCache[label] = true;
        return label;
    }

    可以更加直观地看到,这个 type 函数可以接收一个字符串,在类型缓存对象 typeCache 中检查是否已经设置过这个 Key, 如果已经设置过一次,抛出异常,这样可以避免命名冲突问题。如果没有,则将这个 Action Type 保存为缓存对象的键,值设置为 true. 最后返回这个 Action Type 的字符串。特别需要注意的是它已经被作为一个类型返回了。

    使用 String Literal Type 实现 Action 名称的强类型化

    具体的 Action Type 有哪些呢?它们分别定义在 /app/actions/books.ts/app/actions/layouts.ts 和 /app/actions/collection.ts 中。 

     实现 Action 接口

    然后,我们再看看 Action 接口的定义,它来自 @ngrx/store

    export interface Action {
      readonly type: string;
    }

    这里是对一个 Action 基本的要求,需要一个字符串类型的 Action 名称。

    为了便于使用每一种 Action ,使用了 Action Creator。以后可以直接使用这些 class 来创建 Action,而不用直接创建对象。这里定义了每种 Action 所对应的 Action 实现。例如 SearchAction 的实现如下所示:

    /**
     * Every action is comprised of at least a type and an optional
     * payload. Expressing actions as classes enables powerful
     * type checking in reducer functions.
     *
     * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
     */
    export class SearchAction implements Action {
      type = ActionTypes.SEARCH;
    
      constructor(public payload: string) { }
    }

    可以看到它的 Action 类型使用 ActionTypes 来定义,值固定设置为了 "[Book] Search",可以通过构造函数传递这个 Action 所使用的附加数据。

    ActionTypes 的定义如下所示,实际上是一个对象。我们通过属性来获取实际的值,这个值来自前面的 type 函数返回值。

    /**
     * For each action type in an action group, make a simple
     * enum object for all of this group's action types.
     *
     * The 'type' utility function coerces strings into string
     * literal types and runs a simple check to guarantee all
     * action types in the application are unique.
     */
    export const ActionTypes = {
      SEARCH:           type('[Book] Search'),
      SEARCH_COMPLETE:  type('[Book] Search Complete'),
      LOAD:             type('[Book] Load'),
      SELECT:           type('[Book] Select'),
    };

    其它三种的 Action 以此类推。

     我们先看看最后的 5 行。这里导出类型的别名。

    这里使用了 TypeScript 中的 String Literal Types, 使得我们可以利用强类型的方式来使用字符串。

    /**
     * Export a type alias of all actions in this action group
     * so that reducers can easily compose action types
     */
    export type Actions
      = SearchAction
      | SearchCompleteAction
      | LoadAction
      | SelectAction;

    代码实现

    代码的全部内容如下所示。

    import { Action } from '@ngrx/store';
    import { Book } from '../models/book';
    import { type } from '../util';
    
    /**
     * For each action type in an action group, make a simple
     * enum object for all of this group's action types.
     *
     * The 'type' utility function coerces strings into string
     * literal types and runs a simple check to guarantee all
     * action types in the application are unique.
     */
    export const ActionTypes = {
      SEARCH:           type('[Book] Search'),
      SEARCH_COMPLETE:  type('[Book] Search Complete'),
      LOAD:             type('[Book] Load'),
      SELECT:           type('[Book] Select'),
    };
    
    
    /**
     * Every action is comprised of at least a type and an optional
     * payload. Expressing actions as classes enables powerful
     * type checking in reducer functions.
     *
     * See Discriminated Unions: https://www.typescriptlang.org/docs/handbook/advanced-types.html#discriminated-unions
     */
    export class SearchAction implements Action {
      type = ActionTypes.SEARCH;
    
      constructor(public payload: string) { }
    }
    
    export class SearchCompleteAction implements Action {
      type = ActionTypes.SEARCH_COMPLETE;
    
      constructor(public payload: Book[]) { }
    }
    
    export class LoadAction implements Action {
      type = ActionTypes.LOAD;
    
      constructor(public payload: Book) { }
    }
    
    export class SelectAction implements Action {
      type = ActionTypes.SELECT;
    
      constructor(public payload: string) { }
    }
    
    /**
     * Export a type alias of all actions in this action group
     * so that reducers can easily compose action types
     */
    export type Actions
      = SearchAction
      | SearchCompleteAction
      | LoadAction
      | SelectAction;

    然后是 /app/actions/layouts.ts.

    import { Action } from '@ngrx/store';
    import { type } from '../util';
    
    export const ActionTypes = {
      OPEN_SIDENAV:   type('[Layout] Open Sidenav'),
      CLOSE_SIDENAV:  type('[Layout] Close Sidenav')
    };
    
    
    export class OpenSidenavAction implements Action {
      type = ActionTypes.OPEN_SIDENAV;
    }
    
    export class CloseSidenavAction implements Action {
      type = ActionTypes.CLOSE_SIDENAV;
    }
    
    
    export type Actions
      = OpenSidenavAction
      | CloseSidenavAction;

    以及 /app/actions/collection.ts 的定义。

    import { Action } from '@ngrx/store';
    import { Book } from '../models/book';
    import { type } from '../util';
    
    export const ActionTypes = {
      ADD_BOOK:             type('[Collection] Add Book'),
      ADD_BOOK_SUCCESS:     type('[Collection] Add Book Success'),
      ADD_BOOK_FAIL:        type('[Collection] Add Book Fail'),
      REMOVE_BOOK:          type('[Collection] Remove Book'),
      REMOVE_BOOK_SUCCESS:  type('[Collection] Remove Book Success'),
      REMOVE_BOOK_FAIL:     type('[Collection] Remove Book Fail'),
      LOAD:                 type('[Collection] Load'),
      LOAD_SUCCESS:         type('[Collection] Load Success'),
      LOAD_FAIL:            type('[Collection] Load Fail'),
    };
    
    /**
     * Add Book to Collection Actions
     */
    export class AddBookAction implements Action {
      type = ActionTypes.ADD_BOOK;
    
      constructor(public payload: Book) { }
    }
    
    export class AddBookSuccessAction implements Action {
      type = ActionTypes.ADD_BOOK_SUCCESS;
    
      constructor(public payload: Book) { }
    }
    
    export class AddBookFailAction implements Action {
      type = ActionTypes.ADD_BOOK_FAIL;
    
      constructor(public payload: Book) { }
    }
    
    /**
     * Remove Book from Collection Actions
     */
    export class RemoveBookAction implements Action {
      type = ActionTypes.REMOVE_BOOK;
    
      constructor(public payload: Book) { }
    }
    
    export class RemoveBookSuccessAction implements Action {
      type = ActionTypes.REMOVE_BOOK_SUCCESS;
    
      constructor(public payload: Book) { }
    }
    
    export class RemoveBookFailAction implements Action {
      type = ActionTypes.REMOVE_BOOK_FAIL;
    
      constructor(public payload: Book) { }
    }
    
    /**
     * Load Collection Actions
     */
    export class LoadAction implements Action {
      type = ActionTypes.LOAD;
    
      constructor() { }
    }
    
    export class LoadSuccessAction implements Action {
      type = ActionTypes.LOAD_SUCCESS;
    
      constructor(public payload: Book[]) { }
    }
    
    export class LoadFailAction implements Action {
      type = ActionTypes.LOAD_FAIL;
    
      constructor(public payload: any) { }
    }
    
    export type Actions
      = AddBookAction
      | AddBookSuccessAction
      | AddBookFailAction
      | RemoveBookAction
      | RemoveBookSuccessAction
      | RemoveBookFailAction
      | LoadAction
      | LoadSuccessAction
      | LoadFailAction;

     总结

    • Action 的名称使用了 String Literal Type 来实现强类型支持
    • 针对每种 Action 实现了 Action 接口,其中固定了所对应的 Action 类型,使用中不必再提供这个 Action 串,实际上是 Action Creator。
    • 对于应用中常见的大量 Action 名称冲突问题,通过 type 函数解决,这个函数将会缓存已经定义的 Action 类型。 

    参考资源:

    上一篇:ngRx 官方示例分析 - 1. 介绍

    下一篇:ngRx 官方示例分析 - 3. reducers

  • 相关阅读:
    Java 泛型 泛型的约束与局限性
    Java 泛型 泛型方法
    Java 泛型 泛型数组
    Java 泛型 协变性、逆变性
    Java 泛型 协变式覆盖和泛型重载
    Java 泛型 泛型代码和虚拟机
    Insertion Sort List
    Remove Duplicates from Sorted List II
    String to Integer (atoi)
    SpringMvc源码入门
  • 原文地址:https://www.cnblogs.com/haogj/p/6537064.html
Copyright © 2011-2022 走看看