У меня есть простое приложение со списком задач. Один из экранов - это список «Сегодня и Просрочено».

Редуктор задач выглядит так:

{
   "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]
}

Мой список редукторов выглядит так:

{
   "data": {
      919: {
         "id": 919,
         "name": "Today & Overdue"
      },
      818: {
         "id": 818,
         "summary": "My Cool List"
      }
   },
   "byId": [919, 818]
}

В списке «Сегодня и просроченные» мне нужно получить все задачи, для которых 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 редукторе; что-то вроде byDueDate для отслеживания массива массивов сроков.


{
   "data": ...,
   "byId": ...,
   "byDueDate": {
       "2020-03-19": [112,123,141, ...],
       "2020-03-20": [922, 939, ...],
   }
}

Кеш даты кажется слишком сложным и может быть не синхронизирован.

Каков рекомендуемый способ обработки повторного выбора, который будет:

  • Будет ли фильтрация к задачам, которые должны быть выполнены сегодня или старше
  • Задачи, которые не выполнены
2
Brian Weinreich 22 Мар 2020 в 03:17

2 ответа

Лучший ответ

Если выходные данные селектора являются вычисляемым массивом, который использует 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>
1
Martijn Pieters 31 Мар 2020 в 07:11

Что значит «регенерирует селектор»? Где вы используете этот селектор? Если вы используете его в функциональном компоненте реагирования, например, через hook (useSelector), то:

// 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 будет равным текущему выходу селектора getTasksTodayOrOlder (все значения внутри объектов равны), потому что мы передали второй аргумент shallowEqual нашей функции useSelector. Я использовал карту, чтобы удалить «отслеживание» из ненужных свойств из нашего объекта данных.

И нам нужно разделить наш селектор на два, потому что нам нужно только пересчитать, если наш state.tasks.data изменится, а не когда какая-либо часть нашего состояния изменится.

Кроме того, я думаю, что вы должны использовать endOfDay в качестве значения аргумента для селектора, потому что он динамический.

0
Exclaim 25 Мар 2020 в 12:06