EDIT
Dormivo e sognavo (seriamente haha) con un semplice (ma forse ancora non necessaria) soluzione per questo caso. È necessario restituire un tipo primitivo sul risultato del filtro, quindi è possibile eseguire JSON.stringify()
e JSON.parse()
sul secondo selettore. La seguente suite di test passa:
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 :)
Tuttavia, a seconda del caso d'uso, potrebbe essere più lento del filtro su ogni chiamata. Se verifichi questa performance, condividi con noi.
PRIMO POST
Come ho detto nel comments, il modo in cui hai implementato che rende createSelector
inutile.
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
In primo luogo, ho fatto alcuni adattamenti per risolvere questo problema prima.
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
Ora la tua domanda è valida:
c'è la possibilità di ottenere solo un nuovo oggetto, se vi sono modifiche su oggetti o l'elenco filtrato?
Quello che vuoi è questo, giusto?
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
Ma l'ultima riga avrà esito negativo perché recomputations()
sarebbe 2.
L'unico modo che vedo per risolvere questo sta facendo la memoized filteredList
parte del tuo stato, ma può essere strano.
Succede perché 'filter' (il selettore di input) restituisce sempre un nuovo oggetto. AFAIK, 'reselect' confronta il risultato dei selettori di input tramite' === '(' state.list.filter (filterFunction) === state.list.filter (filterFunction) ') e sarà sempre' false' anche se l'elenco non cambia. –
Per chiarire, il modo in cui l'hai implementato, 'createSelector' finisce per essere inutile. Si ricalcola ad ogni chiamata anche se lo stato non cambia, poiché il selettore di input (filtro) restituisce sempre un nuovo oggetto. –