Selectors¶
В NgRx селекторы представляют собой чистые функции и используются для получения определенных частей глобального состояния. Отличительные особенности селекторов:
- Мобильность;
- Мемоизация;
- Возможность построения композиции селекторов;
- Легкость тестирования.
createSelector()¶
Селекторы создаются с помощью функции NgRx createSelector()
, которая может принимать неограниченное количество функций, каждая из которых возвращает определенную часть состояния. При этом самой последней функции, которая и возвращает конечный результат, в качестве аргументов передаются результаты первых функций.
Для всех примеров этой главы будем использоваться следующее состояние.
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;
}
Пример получения данных состояния.
const selectUsers = (state: State) => state.users;
export const selectUsersList = createSelector(
selectUsers,
(state: UsersState) => state.list
);
Созданные NgRx селекторы могут быть использованы для создания других селекторов (композиция).
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
).
export class AppComponent {
constructor(private store: Store) {
this.store
.pipe(select(selectCountSum))
.subscribe((vl) => console.log(vl));
}
}
Вы также можете совместно с NgRx select()
использовать имеющиеся в RxJS операторы.
this.store
.pipe(
select(selectCountSum),
map((sum) => sum * 2)
)
.subscribe((vl) => console.log(vl));
Для получения состояния на основе данных, отсутствующих в хранилище, вторым параметром функции NgRx select()
передайте эти данные и они будут доступны в последней функции в качестве последнего параметра.
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
);
}
);
И далее в компоненте.
this.store
.pipe(select(selectArticlesByUser, { user_id: 3 }))
.subscribe((vl) => console.log(vl));
createFeatureSelector()¶
Для удобства получения срезов состояния верхнего уровня глобального объекта используйте функцию NgRx createFeatureSelector()
, которая строковым параметром принимает один из верхних ключей.
const selectArticles = createFeatureSelector<State>(
'articles'
);
Мемоизация¶
Мемоизация позволяет избежать повторных вычислений при вызове функции, которая уже вызывалась ранее. При первом вызове запоминается возвращаемое функцией значение, которое будет пересчитано и обновлено при любом изменении в наборе параметров, в противном же случае будет возвращаться сохраненное значение.
Для сброса (удаления) сохраненного значения необходимо вызвать у селектора метод release()
. Настоятельно рекомендуется его использовать, если вычисленные данные занимают в памяти много места и в последующем вам больше не понадобятся.
//получаем и запоминаем все статьи по запрашиваемому user_id
this.store
.pipe(select(selectArticlesByUser, { user_id: 3 }))
.subscribe((vl) => console.log(vl));
//сбрасываем сохраненное значение
selectArticlesByUser.release();