Действия @ngrx с несколькими полезными нагрузками

Я создаю веб-приложение с использованием Angular 4 и @ngrx 4, и у меня возникла проблема с настройкой строго типизированных действий.

Я новичок в @ngrx, и я создал следующий класс для своих действий:

export const ActionTypes = {
  LOAD_PRODUCT_TREE: 'load-product-tree',
  LOAD_PRODUCT_TREE_COMPLETE: 'load-product-tree-complete'
};

// Load product tree by Id.
export class LoadProductTreeAction implements Action {
  type = ActionTypes.LOAD_PRODUCT_TREE;
  constructor (public payload: number) { }
}

export class LoadProductTreeCompleteAction implements Action {
  type = ActionTypes.LOAD_PRODUCT_TREE_COMPLETE;
  constructor (public payload: Map<number, Node>) { }
}

export type Actions = LoadProductTreeAction | LoadProductTreeCompleteAction;

Однако когда я пытаюсь использовать эти действия в своем редукторе:

export interface State {
  productTree: Map<number, Node>;
}

const initialState: State = {
  productTree: new Map<number, Node>()
};

export function productTreeReducer(state = initialState, action: productTreeOperations.Actions): State {

  switch (action.type) {
    case productTreeOperations.ActionTypes.LOAD_PRODUCT_TREE:
      return state;

    case productTreeOperations.ActionTypes.LOAD_PRODUCT_TREE_COMPLETE:
      return { productTree: action.payload };

    default:
      return state;
  }

}

Я получаю эту ошибку: Type '{ productTree: number | Map<number, Node>; }' is not assignable to type 'State'. Types of property 'productTree' are incompatible. Type 'number | Map<number, Node>' is not assignable to type 'Map<number, Node>'. Type 'number' is not assignable to type 'Map<number, Node>'. в этой строке: return { productTree: action.payload };

Как я могу создать набор действий, которые принимают разные полезные данные / аргументы?

Также, что было бы наилучшей практикой для возвращаемого значения действия LOAD_PRODUCT_TREE в редукторе? Должен ли я возвращать текущее состояние, пока информация загружается из серверной части, или я должен возвращать что-то еще?


person Felipe    schedule 02.08.2017    source источник


Ответы (3)


Предполагая, что вы используете интерфейс Action по умолчанию фреймворка, выполните следующий рефакторинг:

export const LOAD_PRODUCT_TREE = 'load-product-tree';
export const LOAD_PRODUCT_TREE_COMPLETE = 'load-product-tree-complete';

export class LoadProductTreeAction implements Action {
  readonly type = LOAD_PRODUCT_TREE;
  constructor (public payload: number) { }
}

export class LoadProductTreeCompleteAction implements Action {
  readonly type = LOAD_PRODUCT_TREE_COMPLETE;
  constructor (public payload: Map<number, Node>) { }
}

export type Actions = LoadProductTreeAction | LoadProductTreeCompleteAction;

export function productTreeReducer(state = initialState, action: productTreeOperations.Actions): State {

  switch (action.type) {
    case productTreeOperations.LOAD_PRODUCT_TREE:
      return state;

    case productTreeOperations.LOAD_PRODUCT_TREE_COMPLETE:
      return { productTree: action.payload };

    default:
      return state;
  }
}

Позже я добавлю соответствующие пояснения, но в основном вы хотите, чтобы машинописный текст распознавал свойство type как отдельный тип, а не как строку.

person Jota.Toledo    schedule 02.08.2017

Ключ к проблеме - readonly ключевое слово

Для типа действия нужно установить только чтение

person Lleohao    schedule 16.02.2019
comment
Постарайтесь быть более конкретным. Если у вас есть такой общий ответ, вставьте его в комментарии, а не в качестве ответа. Прочтите, пожалуйста, правила. - person dpapadopoulos; 16.02.2019

Мне сложно прочитать ваш код и угадать его, но я предполагаю, что у вас есть

export class Action{
    type: ActionType;
    payload: number | Map<number, Node>;
}

Тогда у вас есть функция:

export function productTreeReducer(state = initialState, action: productTreeOperations.Actions): State {...}

Итак, ваша проблема связана с несовместимостью типов. State может принимать только Map<>, но вы фактически возвращаетесь Map<> OR number. И в этом случае тот факт, что в LoadProductTreeCompleteAction вы указали Map<> в конструкторе, не имеет значения, поскольку значение payload может измениться во время выполнения (внешне setter или внутренне изменением состояния / методом [даже если оно может не существовать]).

Поэтому вам нужно изменить подпись вашего State, чтобы принимать оба типа, или, если вы уверены, что LOAD_PRODUCT_TREE_COMPLETE всегда возвращает Map<>, вы можете попробовать

return { productTree: <Map<number, Node>>action.payload };
person A. Tim    schedule 02.08.2017
comment
Привет, поскольку я уверен, что действие LOAD_PRODUCT_TREE_COMPLETE вернет объект Map, я использовал предложенное вами приведение типа: return { productTree: <Map<number, Node>>action.payload };. Спасибо за помощь! - person Felipe; 02.08.2017
comment
Это действительно плохой подход. Во-первых, если у вас есть другие случаи переключения, в которых вы извлекаете информацию о новом состоянии из полезной нагрузки, вам нужно будет реплицировать явное приведение повсюду. Во-вторых, зачем вам определять тип полезной нагрузки как тип объединения, который зависит от типов, которые реализации действия определяют для полезной нагрузки? это просто вздор. @Felipe, пожалуйста, проверьте мой ответ или взгляните на пример приложения в проекте @ ngrx / plattform. - person Jota.Toledo; 02.08.2017
comment
@ Jota.Toledo, я создал этот класс для хранения типов действий, следуя этому руководству: pluralsight.com/guides/front-end-javascript/. Я думаю, что я это неправильно понял. Спасибо за помощь! - person Felipe; 03.08.2017
comment
это руководство не предназначено для [email protected] - person Jota.Toledo; 03.08.2017