Проблема ngrx / entity с выбором данных внутри функции

Я пытаюсь создать библиотеку с функцией состояния и комбинированным редуктором.

Мой редуктор функций выглядит так:

index.ts

export const storageFeatureKey = 'storage';

export interface StorageState {
  [fromBookEntries.bookEntriesFeatureKey]: fromBookEntries.State
}

export function reducers(state: StorageState | undefined, action: Action) {
  return combineReducers({
    [fromBookEntries.bookEntriesFeatureKey]: fromBookEntries.reducer
  })(state, action);
}

bookEntriesFeatures выглядит так:

book-entries.reducer.ts

export const bookEntriesFeatureKey = 'book-entries';

export interface State extends EntityState<WarehouseBookEntry> {
  selectedEntryId: number;
}

export const adapter: EntityAdapter<WarehouseBookEntry> = createEntityAdapter<WarehouseBookEntry>({
  selectId: (entry) => entry.id
});

export const initialState: State = adapter.getInitialState({
  selectedEntryId: null
});

export const reducer = createReducer(
  initialState,
  on(...),
  on(...)
);

export const getSelectedEntryId = (state: State) => state.selectedEntryId;

const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal
} = adapter.getSelectors();

export const getBookEntriesIds = selectIds;
export const getBookEntriesEntities = selectEntities;
export const getAllBookEntries = selectAll;
export const getTotalBookEntries = selectTotal;

В моем функциональном модуле я импортирую StoreModule.forFeature () с моим функциональным ключом и редуктором функций.

Нет, я пытаюсь выбрать запись на основе выбранного идентификатора. Поэтому я запускаю действие, которое успешно добавляет идентификатор выбранной записи в состояние моих записей в книге.

Чтобы выбрать выбранную запись, у меня есть следующий селектор внутри моих селекторов функций:

index.ts

export const selectSelectedBookEntry = createSelector(
  fromBookEntries.getBookEntriesEntities, // fromBookEntries is in my bookEntriesFeature
  selectSelectedBookEntryId, // simply returns the selected id
  (entries, selectedId) => {
    //console.log(entries, selectedId)
    //console.log("fired");
    return entries && entries[selectedId];
  }
);

При загрузке записей через другое отправленное действие селектор вызывается только один раз (перед загрузкой данных). Я обнаружил, что когда я создаю селекторы из моего bookEntriesAdapter внутри своих селекторов функций, это работает. Нравится:

index.ts

export const selectStorageState = createFeatureSelector<StorageState>(
  storageFeatureKey
);

export const selectBookEntriesState = createSelector(
  selectStorageState,
  state => state[fromBookEntries.bookEntriesFeatureKey]
);

const {
  selectIds,
  selectEntities: selectBookEntriesEntities,
  selectAll,
  selectTotal
} = fromBookEntries.adapter.getSelectors(selectBookEntriesState);

export const selectAllBookEntries = createSelector(
  selectBookEntriesState,
  selectAll
)

export const selectSelectedBookEntry = createSelector(
  selectBookEntriesEntities,
  selectSelectedBookEntryId,
  (entries, selectedId) => {
    //console.log(entries, selectedId)
    //console.log("fired");
    return entries && entries[selectedId];
  }
);

Но почему? Я немного потерялся, пытаясь понять, почему это не работает наоборот.

Приведенный выше пример, который не работает, взят из официального документа ngrx (https://ngrx.io/guide/entity/adapter). Тот, который работает, взят из примера кода ngrx / platform (https://github.com/ngrx/platform/blob/master/projects/example-app/src/app/books/reducers/index.ts).

Я не думаю, что это необходимо, но я не расширяю состояние root-app-store, поскольку у меня нет доступа к нему, потому что это библиотека (пытаясь обойти дизайн, управляемый доменом, и я создал feature-lib для решения такого рода проблем). Или это может быть проблема?

Я был бы признателен, если бы кто-нибудь мог объяснить это немного подробнее.


person Urastor    schedule 31.03.2020    source источник
comment
поделитесь, пожалуйста, кодом ваших селекторов.   -  person satanTime    schedule 08.04.2020


Ответы (1)


В вашем коде вы упускаете createFeatureSelector и его интеграцию.

Важно сначала выбрать состояние вашей функции из глобального состояния, а затем выбрать нужные данные.

когда вы используете такие селекторы

const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal
} = adapter.getSelectors();

export const getBookEntriesEntities = selectEntities;

они ничего не знают о StoreModule.forFeature(FEATURE_KEY), и вы обязаны сузить для них глобальное состояние.

Для этого вам понадобится createFeatureSelector, он выбирает вашу функцию из глобального состояния.

Следовательно, чтобы использовать getBookEntriesEntities, вам нужно сначала выбрать свою функцию, а затем передать эти данные в getBookEntriesEntities.

const selectMyFeature = createFeatureSelector(FEATURE_KEY);

const selectMyFeatureEntities = createSelector(
  selectMyFeature,
  getBookEntriesEntities,
);

теперь, когда мы используем store.select(selectMyFeatureEntities), он знает, как выбирать данные из функции.

Правильный код в вашем случае будет


const {
  selectIds,
  selectEntities,
  selectAll,
  selectTotal
} = adapter.getSelectors();

// put here feature name from .forFeature
const selectMyFeature = createFeatureSelector(FEATURE_KEY);

export const selectMyFeatureBooks = createSelector(
  selectMyFeature,
  state => state[fromBookEntries.bookEntriesFeatureKey]
);

export const getSelectedEntryId = createSelector(
  selectMyFeatureBooks,
  (state: State) => state.selectedEntryId,
);

export const getBookEntriesIds = createSelector(
  selectMyFeatureBooks,
  selectIds,
);
export const getBookEntriesEntities = createSelector(
  selectMyFeatureBooks,
  selectEntities,
);
export const getAllBookEntries = createSelector(
  selectMyFeatureBooks,
  selectAll,
);
export const getTotalBookEntries = createSelector(
  selectMyFeatureBooks,
  selectTotal
);

и теперь selectSelectedBookEntry будет работать как положено.

person satanTime    schedule 20.05.2020