Селекторы NGRX: фабричный селектор в другом селекторе без опоры в методе createSelector
Использование заводского шаблона селектора
const selectA = (id: number) => createSelector(...)
У меня есть экземпляр, в котором я хочу повторно использовать этот селектор в другом селекторе (который выполняет итерацию по массиву идентификаторов), но я не знаю, какое значение нужно передать селектору фактора при вызове.
Итак, у меня есть селектор, который я использую всякий раз, когда хочу получить фрагмент состояния для компонента A.
const selectA = (id: number) =>
createSelector(
selector1.selectEntityMap,
selector2.selectEntityMap,
selector3ById(id),
(
thing1,
thing2,
thing3
) => {
return ...
});
Теперь я хочу получить список компонента A для каждого элемента в массиве.
const selectListOfA = (ids: number[]) =>
createSelector(
selectA,
(selectorA) => {
return ids.map((id) => selectorA(id));
});
Проблема в
selectA
, который теперь является заводским селектором, ожидает параметр, но я не знаю его при вызове.
Я могу получить код для компиляции, создав еще одну фабрику на фабрике
const selectAFactory = () => selectA;
Затем укажите новую фабрику в
createSelector
const selectListOfA = (ids: number[]) =>
createSelector(
selectAFactory, <<< here
(selectorA) => {
return ids.map((id) => selectorA(id));
});
Но, конечно, сейчас происходит то, что селектор возвращает список
MemoizedSelector[]
.
Этот шаблон не кажется таким сложным, люди не используют повторно свои селекторы таким образом, что мне не хватает?
1 ответ
Функция, возвращаемая является стандартной функцией, т.е. в ней нет ничего волшебного, как хорошо объяснено в этом сообщении в блоге: https://dev.to/zackderose/ngrx-fun-with-createselectorfactory-hng
Это означает, что можно просто вызвать функцию, возвращаемую из для каждого идентификатора, и будет возвращен массив срезов состояния для компонента A:
export const selectListOfA = (ids: number[]) =>
createSelector(
(state) => state,
(state) => ids.map((id) => selectA(id)(state))
);
Это работает, как и ожидалось, но поскольку функция проектора будет выполняться каждый раз, когда что-либо изменяется в хранилище (воссоздание селектора для каждого идентификатора), это решение будет иметь серьезные проблемы с производительностью.
С тем же успехом мы могли бы упростить код до такой же низкой производительности:
const selectListOfA = (ids: number[]) =>
(state) => ids.map(
(id: number) => selectA(id)(state)
);
Если вместо этого мы предоставим массив селекторов в качестве входных данных для
createSelector
вызов, то Ngrx сможет правильно определить, когда ему нужно переоценить
selectA
селекторы:
const selectListOfA = (ids: number[]) =>
createSelector(
ids.map((id) => selectA(id)), // This results in an array of selectors
(...resultArr) => resultArr
);
Однако Typescript будет жаловаться, поскольку метод createSelector не имеет соответствующей перегрузки, объявленной для массива переменной длины, поэтому нам нужно ослабить входной тип массива (чтобы
any
), а также указать тип возвращаемого значения
selectListOfA
.
Ответ на вопрос такой:
const selectListOfA = (ids: number[]) =>
(createSelector(
ids.map((id) => selectA(id)) as any,
(...resultArr) => resultArr
) as (state) => string[]);