NGRX - Использование хешей вместо массивов для состояния - PullRequest
0 голосов
/ 16 сентября 2018

на самом деле я использую NGRX (Redux для Angular). Сначала я начал хранить свои сущности в виде массивов внутри моего состояния. После прочтения некоторых статей я хочу сохранить свои сущности в виде хэшей, в то время как я считаю, что более эффективный доступ к сущностям сущностей с использованием идентификатора вместо фильтрации массива.

Но я на самом деле не уверен, правильно ли это делать.

Не стесняйтесь проверить мой (рабочий, но лучший метод?) Образец здесь: Образец статей Ngrx

Внутри article.component вы найдете articleReducer и ArticlesComponent, которые будут использовать магазин, чтобы получить все статьи и получить одну конкретную статью.

Для каждой неуверенной части я комментирую это с помощью QUESTION x - ...

Если вы не хотите проверять полный образец, вот фрагменты вопросов:

Вопрос 0:

export const initialState: ArticleState = {
  foo: 'bar',
  articlesById: [], // QUESTION 0: What is a good usecase to hold an array with the id's when Object.keys(hash) will give the same?
  articleEntities: {}
}

Вопрос 1 + 2:

case GET_ALL_ARTICLES: {

// QUESTION 1: is this a good choice? Or is there a better alternative to create the hash?
   const articleHash: { [id: number]: Article} = {};
   action.payload.forEach(article => articleHash[article.id] = article);

  return {
    ...state,

    // QUESTION 2: is this a good choice? Or is there a better alternative?
    articlesById: action.payload.map(article => article.id),
    articleEntities: articleHash
  }
}

Вопрос 3:

// QUESTION 3 - Getting all articleEntities as Array for ngFor : is this a      good choice? Or is there a better alternative?    
  this.articles$ = this.store.pipe(
    select('articles', 'articleEntities'),
    map(entities => Object.keys(entities).map(id => entities[id]))
  );

Вопрос 4:

const givenIdByRouterMock = 2;
// QUESTION 4 - Getting one concrete Article for given id : is this a good choice? Or is there a better alternative?    
this.article$ = this.store.pipe(
  select('articles', 'articleEntities'),
  map(entities => entities[givenIdByRouterMock])
);

Если я обновлю какую-то статью (например, с идентификатором 2), используя новое действие, я хочу, чтобы компонент, включающий статью, был обновлен.

Это допустимые варианты или есть лучшие варианты, решения? Не стесняйтесь раскошелиться на образец stackblitz с другими вариантами / фрагментами кода.

Спасибо!

Ответы [ 2 ]

0 голосов
/ 16 сентября 2018

Если порядок имеет значение - используйте Array в качестве свойства, порядок в объектах не гарантируется . Для оптимизации поиска в массиве мы можем создать селектор, который преобразует массив в объект. Лучше поддерживать нормализованное состояние (нет необходимости сохранять значение, которое мы можем вычислить из других).

export interface ArticleState {
  foo: string;
  articles: Article[];
}

export const selectFeature = (state) => state.articleState;
export const selectFeatureArticles = createSelector(selectFeature, state => state.articles);
export const selectFeatureArticlesMap = createSelector(
  selectFeatureArticles,
  articles => _.keyBy(articles, 'id' )
);

ВОПРОС 0: ​​Какой хороший вариант для хранения массива с идентификаторами, когда Object.keys (hash) выдаст то же самое?

используйте Array и селектор, которые преобразуют Array в Object

ВОПРОС 1: это хороший выбор? Или есть лучшая альтернатива создать хеш?

action.payload.reduce((hash, article) =>({...hash, [article.id]: article}), {});

или Ключ LodashBy

_.keyBy(action.payload, 'id' );

ВОПРОС 2: это хороший выбор? Или есть лучшая альтернатива?

Все нормально.

ВОПРОС 3 - Получение всех articleEntities в виде массива для ngFor: это хороший выбор? Или есть лучшая альтернатива?

Хранить articles в массиве.

ВОПРОС 4 - Получение одной конкретной статьи для данного идентификатора: это хороший выбор? Или есть лучшая альтернатива?

Я не в порядке.

0 голосов
/ 16 сентября 2018

Q0: свойство ids может использоваться для указания порядка. Еще один момент: если вы удаляете сущность, достаточно удалить ее только из массива this, что проще по сравнению с ее удалением из словаря.

Q1: Ваше решение работает, я предпочитаю использовать снижение

Q2: отлично

return {
  products: action.payload.products.reduce((dict, product) => {
    dict[product.sku] = product;
    return dict;
  }, {}),
  productSkus: action.payload.products.map(product => product.sku),
};

Q3: в ваших селекторах вы действительно должны преобразовать его в массив. Если вы используете свойство ids (см. Ответ1), вы должны использовать ids.map(id => entities[id]).

Q4: действительно, чтобы выбрать конкретную сущность, используйте entities[id]

Дополнительно:

  • NgRx имеет пакет @ ngrx / entity , который обрабатывает состояние таким образом - unsorted_state_adapter.ts - state_selectors.ts

  • Вместо выбора данных с помощью this.store.pipe(select('articles', 'articleEntities')), я думаю, вы должны взглянуть на NgRx селекторы , которые чище imho.

  • Начиная с Angular 6.1, вы также можете использовать ngFor над словарем с символьным каналом.

.

<mat-nav-list>
      <a *ngFor="let member of familyMembers | async | keyvalue" 
        mat-list-item [routerLink]="['/groceries', member.key]"> 
          {{ member.value.avatar }} {{member.value.name}} 
      </a>
</mat-nav-list>
...