Как использовать reselect в изменяющемся списке, когда объекты одинаковы?

Я использую reselect для получения частей моего состояния redux. у меня есть список объектов в моем государстве. Одним из моих подселекторов для моего селектора создания является функция фильтра этого списка:

state => state.list.filter(filterFunction)

поэтому я передаю это моему createSelector:

createSelector(
  state => state.list.filter(filterFunction),
  (subsetOfObjects) => subsetOfObjects.map(doSomething)
);

эта функция фильтра возвращает подмножество объектов в списке. Поэтому, если список изменяется, reselect всегда возвращает новый объект, даже если подмножество не изменилось, потому что список не тот же (полностью правильный).

есть ли возможность получить только новый объект, если есть изменения в объектах или в отфильтрованный список?

2 ответов


ведь у меня была идея, которая могла сработать:

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,
);

строку ...order.map(id => list[id]), будет распространять объекты в качестве аргументов. Они будут одинаковыми, если Order-array не будет изменен. Поэтому я могу создать новый объект только с объектами, перечисленными в порядке.


редактировать

Я спал и видел сон (серьезно ха-ха) с простым (но, возможно, все еще ненужным) решением для этого случая. Вы должны вернуть примитивный тип в результате фильтра, чтобы вы могли 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 :)

однако, в зависимости от вашего варианта использования, он может быть медленным, чем фильтр при каждом вызове. Если вы протестируете эту производительность, поделитесь с США.


ПЕРВЫЙ ПОСТ

как я сказал в комментарии, то, как вы реализовали это делает 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 часть вашего состояния, но это может быть странно.