2016-06-20 43 views
5

Io uso riseleziona per ottenere parti del mio stato di redux. ho una lista di oggetti nel mio stato. Uno dei miei subselector per il mio creare selettore è una funzione di filtro di questa lista:Come riselezionare su un elenco di modifiche, quando gli oggetti sono gli stessi?

state => state.list.filter(filterFunction) 

Così mi passa questo al mio createSelector:

Questa funzione di filtro restituisce un sottoinsieme dei miei oggetti nella lista . Quindi, se la lista cambia, riseleziona sempre un nuovo oggetto, anche se il sottoinsieme non è cambiato, perché la lista non è la stessa (completamente corretta).

c'è la possibilità di ottenere solo un nuovo oggetto se ci sono modifiche agli oggetti o all'elenco filtrato?

+0

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. –

+0

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. –

risposta

3

Dopo tutto ho avuto un'idea che potrebbe funzionare:

const list = { 


object1: { 
    id: 'object1', 
    }, 
    object2: { 
    id: 'object2', 
    }, 
    object3: { 
    id: 'object3', 
    }, 
}; 

const order = ['object1', 'object3']; 

const selector = (...args) => args.reduce((prev, curr) => ({...prev, [curr.id]: curr}), {}); 

createSelector(
    ...order.map(id => list[id]), 
    selector, 
); 

La linea ...order.map(id => list[id]), si diffonderà gli oggetti come argomenti. Saranno gli stessi se l'array dell'ordine non verrà modificato. Quindi posso generare un nuovo oggetto con solo gli oggetti elencati nell'ordine.

0

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.