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

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
20
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();

Комментарии