EDIT
я спал и мечтал (серьезно ха-ха) с простым (но, возможно, до сих пор ненужным) решением для этого случая. Вы должны вернуть примитивный тип в результат фильтра, так что вы можете JSON.stringify()
и JSON.parse()
на втором селекторе. Следующий комплект тестов проходит:
const state = {
list: [
{ id: 1, active: true },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: false }
]
}
const getFilteredListIds = createSelector(
(state) => JSON.stringify(state.list.filter((object) => !!object.active)),
(filteredList) => JSON.parse(filteredList).map((object) => object.id)
)
expect(getFilteredListIds.recomputations()).toEqual(0)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1)
const newState = {
list: [
...state.list,
{ id: 5, active: false } // should not change subset
]
}
expect(getFilteredListIds(newState)).toEqual([1]) // subset didn't change
expect(getFilteredListIds.recomputations()).toEqual(1) // pass :)
Однако, в зависимости от вашего варианта использования, он может быть медленным, чем фильтр при каждом вызове. Если вы проверите эту производительность, поделитесь с нами.
ПЕРВЫЙ ПОСТ
Как я уже говорил в comments, как вы реализовали, что делает createSelector
бесполезно.
const state = {
list: [
{ id: 1, active: true },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: false }
]
}
const getFilteredListIds = createSelector(
(state) => state.list.filter((object) => !!object.active),
(filteredList) => filteredList.map((object) => object.id)
)
expect(getFilteredListIds.recomputations()).toEqual(0)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1) // fail
Во-первых, я сделал некоторые адаптации, чтобы решить эту первую проблему.
const state = {
list: [
{ id: 1, active: true },
{ id: 2, active: false },
{ id: 3, active: false },
{ id: 4, active: false }
]
}
const getList = (state) => state.list
// it makes filter only happen if state.list changes
const getFilteredList = createSelector(
getList,
(list) => list.filter((object) => !!object.active)
)
const getFilteredListIds = createSelector(
getFilteredList,
(filteredList) => filteredList.map((object) => object.id)
)
expect(getFilteredListIds.recomputations()).toEqual(0)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1)
expect(getFilteredListIds(state)).toEqual([1])
expect(getFilteredListIds.recomputations()).toEqual(1)
// everything pass
Теперь ваш вопрос правомерен:
есть возможность получить только новый объект, если есть изменения в объекты или отфильтрованный список?
Что вы хотите, так?
const newState = {
list: [
...state.list,
{ id: 5, active: false } // should not change subset
]
}
expect(getFilteredListIds(newState)).toEqual([1]) // subset didn't change
expect(getFilteredListIds.recomputations()).toEqual(1) // fail
Но последняя линия потерпит неудачу, потому что recomputations()
будет 2.
Единственный способ я вижу в решении это делает memoized filteredList
часть вашего состояния, но это может быть странно.
Это происходит потому, что 'filter' (ваш селектор ввода) всегда возвращает новый объект. AFAIK, 'reselect' сравнивает результат селекторов ввода с помощью' === '(' state.list.filter (filterFunction) === state.list.filter (filterFunction) '), и он всегда будет' false', даже если список не изменяется. –
Чтобы пояснить, как вы реализовали это, 'createSelector' оказывается бесполезным. Он будет пересчитывать каждый вызов, даже если состояние не изменяется, поскольку селектор ввода (фильтр) всегда возвращает новый объект. –