当对象相同时,如何在更改列表上使用重新选择?
How to use reselect on a changing list, when the objects are the same?
我使用 reselect 来获取部分 redux 状态。我有一份我所在州的物品清单。我的创建选择器的子选择器之一是此列表的过滤器功能:
state => state.list.filter(filterFunction)
所以我将其传递给我的 createSelector:
createSelector(
state => state.list.filter(filterFunction),
(subsetOfObjects) => subsetOfObjects.map(doSomething)
);
此过滤器函数 returns 列表中我的对象的子集。因此,如果列表更改,请始终重新选择 returns 个新对象,即使子集没有更改,因为列表不一样(完全正确)。
如果对象或筛选列表发生变化,是否有可能只获取一个新对象?
编辑
我在睡觉时梦见了(说真的哈哈)这个案例的简单(但可能仍然不必要)解决方案。您必须 return 过滤结果的基本类型,因此您可以 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 :)
但是,根据您的用例,每次调用时它可能比过滤慢。如果您测试了该性能,请与我们分享。
第一个POST
正如我在 中所说,您实施的方式使 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
现在你的问题是有效的:
is there a possibility to get only a new object if there are changes to the objects or the filtered list?
你要的是这个对吧?
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
作为您状态的一部分,但这可能很奇怪。
毕竟我有一个可行的想法:
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 数组对象的指针保持不变。所以我们总是得到与第二个评估函数的参数相同的对象。这可以防止不需要的更新。
只需使用 re-reselect.
re-reselect 是 Reselect 的轻量级包装器,旨在通过更深入的记忆和缓存管理来增强选择器。
我使用 reselect 来获取部分 redux 状态。我有一份我所在州的物品清单。我的创建选择器的子选择器之一是此列表的过滤器功能:
state => state.list.filter(filterFunction)
所以我将其传递给我的 createSelector:
createSelector(
state => state.list.filter(filterFunction),
(subsetOfObjects) => subsetOfObjects.map(doSomething)
);
此过滤器函数 returns 列表中我的对象的子集。因此,如果列表更改,请始终重新选择 returns 个新对象,即使子集没有更改,因为列表不一样(完全正确)。
如果对象或筛选列表发生变化,是否有可能只获取一个新对象?
编辑
我在睡觉时梦见了(说真的哈哈)这个案例的简单(但可能仍然不必要)解决方案。您必须 return 过滤结果的基本类型,因此您可以 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 :)
但是,根据您的用例,每次调用时它可能比过滤慢。如果您测试了该性能,请与我们分享。
第一个POST
正如我在 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
现在你的问题是有效的:
is there a possibility to get only a new object if there are changes to the objects or the filtered list?
你要的是这个对吧?
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
作为您状态的一部分,但这可能很奇怪。
毕竟我有一个可行的想法:
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 数组对象的指针保持不变。所以我们总是得到与第二个评估函数的参数相同的对象。这可以防止不需要的更新。
只需使用 re-reselect.
re-reselect 是 Reselect 的轻量级包装器,旨在通过更深入的记忆和缓存管理来增强选择器。