Перейти к содержанию

Selectors

В NgRx селекторы представляют собой чистые функции и используются для получения определенных частей глобального состояния. Отличительные особенности селекторов:

  • Мобильность;
  • Мемоизация;
  • Возможность построения композиции селекторов;
  • Легкость тестирования.

createSelector()

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

Для всех примеров этой главы будем использоваться следующее состояние.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
export interface User {
  id: number;
  name: string;
  email: string;
}

interface Article {
  id: number;
  user_id: number;
  title: string;
}

interface UsersState {
  list: { [id: number]: User };
  count: number;
}

interface ArticlesState {
  list: { [id: number]: Article };
  count: number;
}

export interface State {
  users: UsersState;
  articles: ArticlesState;
}

Пример получения данных состояния.

1
2
3
4
5
6
const selectUsers = (state: State) => state.users;

export const selectUsersList = createSelector(
  selectUsers,
  (state: UsersState) => state.list
);

Созданные NgRx селекторы могут быть использованы для создания других селекторов (композиция).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
const selectUsers = (state: State) => state.users;

export const selectUsersCount = createSelector(
  selectUsers,
  (state: UsersState) => state.count
);

const selectArticles = (state: State) => state.articles;

export const selectArticlesCount = createSelector(
  selectArticles,
  (state: ArticlesState) => state.count
);

export const selectCountSum = createSelector(
  selectUsersCount,
  selectArticlesCount,
  (usersCount, articlesCount) => usersCount + articlesCount
);

select()

Для использования селектор необходимо передать функции NgRx select(), которая вызывается в методе pipe() хранилища (экземпляра объекта Store).

1
2
3
4
5
6
7
export class AppComponent {
  constructor(private store: Store) {
    this.store
      .pipe(select(selectCountSum))
      .subscribe((vl) => console.log(vl));
  }
}

Вы также можете совместно с NgRx select() использовать имеющиеся в RxJS операторы.

1
2
3
4
5
6
this.store
  .pipe(
    select(selectCountSum),
    map((sum) => sum * 2)
  )
  .subscribe((vl) => console.log(vl));

Для получения состояния на основе данных, отсутствующих в хранилище, вторым параметром функции NgRx select() передайте эти данные и они будут доступны в последней функции в качестве последнего параметра.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
const selectArticles = (state: State) => state.articles;

export const selectArticlesList = createSelector(
  selectArticles,
  (state: ArticlesState) => state.list
);

export const selectArticlesByUser = createSelector(
  selectArticlesList,
  (articles, props) => {
    return articles.filter(
      (item) => item.user_id === props.user_id
    );
  }
);

И далее в компоненте.

1
2
3
this.store
  .pipe(select(selectArticlesByUser, { user_id: 3 }))
  .subscribe((vl) => console.log(vl));

createFeatureSelector()

Для удобства получения срезов состояния верхнего уровня глобального объекта используйте функцию NgRx createFeatureSelector(), которая строковым параметром принимает один из верхних ключей.

1
2
3
const selectArticles = createFeatureSelector<State>(
  'articles'
);

Мемоизация

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

Для сброса (удаления) сохраненного значения необходимо вызвать у селектора метод release(). Настоятельно рекомендуется его использовать, если вычисленные данные занимают в памяти много места и в последующем вам больше не понадобятся.

1
2
3
4
5
6
7
//получаем и запоминаем все статьи по запрашиваемому user_id
this.store
  .pipe(select(selectArticlesByUser, { user_id: 3 }))
  .subscribe((vl) => console.log(vl));

//сбрасываем сохраненное значение
selectArticlesByUser.release();

Комментарии