12

我使用 reselect 来获取我的部分 redux 状态。我有一个我所在州的对象列表。我的创建选择器的子选择器之一是此列表的过滤器功能:

state => state.list.filter(filterFunction)

所以我将它传递给我的 createSelector:

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

此过滤器函数返回列表中我的对象的子集。因此,如果列表发生更改,即使子集没有更改,重新选择也始终返回一个新对象,因为列表不一样(完全正确)。

如果对象或过滤列表发生更改,是否有可能只获得一个新对象?

4

3 回答 3

7

毕竟我有一个可行的想法:

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(
  state => state.order,
  order => createSelector(
    ...order.map(id => state => state.list[id]),
    selector,
  )
);

该行将...order.map(id => state => state.list[id]),对象作为参数展开。如果 order-array 不会更改,它们将是相同的。所以我可以生成一个只包含按顺序列出的对象的新对象。

只有当订单数组发生变化时,才会调用第一个创建选择器的评估函数。如果发生这种情况,无论如何都需要重新计算结果。所以这很好。第二个只有在获得新值时才会重新计算。这些值是从 order 数组中生成的函数。即使列表对象发生更改(由于当前列表中未考虑的其他对象的更多条目或较小更改),指向订单数组对象的指针保持不变。所以我们总是得到一个相同的对象作为我们的第二个评估函数的参数。这可以防止不需要的更新。

于 2016-11-09T14:42:41.323 回答
0

只需使用re-reselect

re-reselect是围绕 Reselect 的轻量级包装器,旨在通过更深入的记忆和缓存管理来增强选择器。

于 2019-11-05T10:00:51.177 回答
-2

编辑

对于这种情况,我正在睡觉和做梦(真的哈哈),用一个简单(但可能仍然没有必要)的解决方案。您必须在过滤器结果上返回一个原始类型,这样您就可以在第二个选择器上返回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。

我认为解决这个问题的唯一方法是让filteredList你的状态成为记忆的一部分,但这可能很奇怪。

于 2016-06-20T12:08:13.030 回答