在基于对象的缩减器上使用带有日期字段的重新选择
Using reselect with a date field on an object-based reducer
我有一个简单的任务列表应用程序。其中一个屏幕是 "Today & Overdue" 列表。
任务减速器看起来像:
{
"data": {
123: {
"id": 123,
"summary": "blah blah",
"dueDate": "2020-03-12",
"completed": true
},
456: {
"id": 456,
"summary": "some other task",
"dueDate": "2020-03-12",
"completed": false
}
},
"byId": [123, 456]
}
我的 list reducer 看起来像:
{
"data": {
919: {
"id": 919,
"name": "Today & Overdue"
},
818: {
"id": 818,
"summary": "My Cool List"
}
},
"byId": [919, 818]
}
在 "Today & Overdue" 列表中,我需要获取 dueDate
今天或更早的所有任务。我尝试使用重新选择来优化列表屏幕的性能,方法是:
# Get end of day today
const now = moment();
const endOfDay = Date.parse(now.endOf("day").utc(true).utcOffset(0).format());
const getTasksTodayOrOlder = (state) => Object.values(state.tasks.data).filter(task => Date.parse(task.dueDate) <= endOfDay);
但似乎只要任务数据中的某个字段发生更改(即已完成或摘要),getTasksTodayOrOlder
就会重新生成选择器。
这是在 tasks
reducer 上保留缓存的唯一方法; byDueDate
之类的东西来跟踪一组截止日期数组。
{
"data": ...,
"byId": ...,
"byDueDate": {
"2020-03-19": [112,123,141, ...],
"2020-03-20": [922, 939, ...],
}
}
日期缓存似乎开销很大,可能会不同步。
推荐的处理重新选择的方法是:
- 将过滤到今天或更早到期的任务
- 未完成的任务
"regenerates the selector"是什么意思?你在哪里使用这个选择器?例如,如果您通过钩子(useSelector)在 React 功能组件中使用它,那么:
// selectors
const now = moment();
const endOfDay = Date.parse(now.endOf("day").utc(true).utcOffset(0).format());
const getTasks = (state) => state.tasks.data;
const getTasksTodayOrOlder = createSelector(getTasks, tasks =>
Object.values(tasks)
.filter(task => Date.parse(task.dueDate) <= endOfDay)
.map(({ id, dueDate }) => {id, dueDate});
// component
import { shallowEqual, useSelector } from 'react-redux';
.
.
.
const tasksTodayOrOlderList = useSelector(getTasksTodayOrOlder, shallowEqual);
任何时候 state.tasks.data
中的某些内容发生变化,getTasksTodayOrOlder
都会重新计算,但如果 tasksTodayOrOlderList
的先前状态与 [=12] 的当前输出一样浅,您将不会重新渲染=] 选择器(对象内的所有值都相等),因为我们将第二个参数 shallowEqual
传递给我们的 useSelector
函数。我使用 map 从我们的数据对象中删除了不必要的属性 "tracking"。
而且我们需要将我们的选择器一分为二,因为我们只需要在 state.tasks.data
发生变化时重新计算,而不是在状态的任何部分发生变化时重新计算。
此外,我认为您应该使用 endOfDay
作为选择器的参数值,因为它是动态的。
如果选择器的输出是使用 Object.keys、Object.values 或 Array.prototype.filter 的计算数组,那么您可以通过以下方式记忆它:
const { createSelector, defaultMemoize } = Reselect;
const state = [
{ val: 1 },
{ val: 2 },
{ val: 3 },
{ val: 4 },
{ val: 5 },
{ val: 6 },
{ val: 7 },
];
//pass an array to memArray like [a,b], as long as a and b are the same
// you will get the same array back even if the arrays themselves
// are not the same like when you use filter, Object.values or Object.keys
const memArray = (() => {
const mem = defaultMemoize((...args) => args);
//pass the array that this function receives to a memoized function
// as separate arguments so if [a,b] is received it'll call
// memoized(a,b)
return arr => mem(...arr);
})();//this is called an IIFE
const selectHigher = createSelector(
state => state,
(_, min) => min,
(data, min) =>
memArray(
Object.values(data).filter(({ val }) => val > min)
)
);
const one = selectHigher(state, 5);
const twoState = [...state, { val: 0 }];
const two = selectHigher(twoState, 5);
console.log('two is:',two);
console.log('one and two are equal', one === two);
const threeState = [...state, { val: 8 }];
const three = selectHigher(threeState, 5);
console.log('three is:',three);
console.log('three and two are equal', three === two);
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>
我有一个简单的任务列表应用程序。其中一个屏幕是 "Today & Overdue" 列表。
任务减速器看起来像:
{
"data": {
123: {
"id": 123,
"summary": "blah blah",
"dueDate": "2020-03-12",
"completed": true
},
456: {
"id": 456,
"summary": "some other task",
"dueDate": "2020-03-12",
"completed": false
}
},
"byId": [123, 456]
}
我的 list reducer 看起来像:
{
"data": {
919: {
"id": 919,
"name": "Today & Overdue"
},
818: {
"id": 818,
"summary": "My Cool List"
}
},
"byId": [919, 818]
}
在 "Today & Overdue" 列表中,我需要获取 dueDate
今天或更早的所有任务。我尝试使用重新选择来优化列表屏幕的性能,方法是:
# Get end of day today
const now = moment();
const endOfDay = Date.parse(now.endOf("day").utc(true).utcOffset(0).format());
const getTasksTodayOrOlder = (state) => Object.values(state.tasks.data).filter(task => Date.parse(task.dueDate) <= endOfDay);
但似乎只要任务数据中的某个字段发生更改(即已完成或摘要),getTasksTodayOrOlder
就会重新生成选择器。
这是在 tasks
reducer 上保留缓存的唯一方法; byDueDate
之类的东西来跟踪一组截止日期数组。
{
"data": ...,
"byId": ...,
"byDueDate": {
"2020-03-19": [112,123,141, ...],
"2020-03-20": [922, 939, ...],
}
}
日期缓存似乎开销很大,可能会不同步。
推荐的处理重新选择的方法是:
- 将过滤到今天或更早到期的任务
- 未完成的任务
"regenerates the selector"是什么意思?你在哪里使用这个选择器?例如,如果您通过钩子(useSelector)在 React 功能组件中使用它,那么:
// selectors
const now = moment();
const endOfDay = Date.parse(now.endOf("day").utc(true).utcOffset(0).format());
const getTasks = (state) => state.tasks.data;
const getTasksTodayOrOlder = createSelector(getTasks, tasks =>
Object.values(tasks)
.filter(task => Date.parse(task.dueDate) <= endOfDay)
.map(({ id, dueDate }) => {id, dueDate});
// component
import { shallowEqual, useSelector } from 'react-redux';
.
.
.
const tasksTodayOrOlderList = useSelector(getTasksTodayOrOlder, shallowEqual);
任何时候 state.tasks.data
中的某些内容发生变化,getTasksTodayOrOlder
都会重新计算,但如果 tasksTodayOrOlderList
的先前状态与 [=12] 的当前输出一样浅,您将不会重新渲染=] 选择器(对象内的所有值都相等),因为我们将第二个参数 shallowEqual
传递给我们的 useSelector
函数。我使用 map 从我们的数据对象中删除了不必要的属性 "tracking"。
而且我们需要将我们的选择器一分为二,因为我们只需要在 state.tasks.data
发生变化时重新计算,而不是在状态的任何部分发生变化时重新计算。
此外,我认为您应该使用 endOfDay
作为选择器的参数值,因为它是动态的。
如果选择器的输出是使用 Object.keys、Object.values 或 Array.prototype.filter 的计算数组,那么您可以通过以下方式记忆它:
const { createSelector, defaultMemoize } = Reselect;
const state = [
{ val: 1 },
{ val: 2 },
{ val: 3 },
{ val: 4 },
{ val: 5 },
{ val: 6 },
{ val: 7 },
];
//pass an array to memArray like [a,b], as long as a and b are the same
// you will get the same array back even if the arrays themselves
// are not the same like when you use filter, Object.values or Object.keys
const memArray = (() => {
const mem = defaultMemoize((...args) => args);
//pass the array that this function receives to a memoized function
// as separate arguments so if [a,b] is received it'll call
// memoized(a,b)
return arr => mem(...arr);
})();//this is called an IIFE
const selectHigher = createSelector(
state => state,
(_, min) => min,
(data, min) =>
memArray(
Object.values(data).filter(({ val }) => val > min)
)
);
const one = selectHigher(state, 5);
const twoState = [...state, { val: 0 }];
const two = selectHigher(twoState, 5);
console.log('two is:',two);
console.log('one and two are equal', one === two);
const threeState = [...state, { val: 8 }];
const three = selectHigher(threeState, 5);
console.log('three is:',three);
console.log('three and two are equal', three === two);
<script src="https://cdnjs.cloudflare.com/ajax/libs/reselect/4.0.0/reselect.min.js"></script>