Можем ли мы использовать мемоизацию Reselect с redux для каждого отдельного селектора (даже для самых простых геттеров)?

Я новичок в Redux и читал о нем неплохие вещи, включая технику запоминания (особенно с повторным выбором). Однако у меня есть вопрос, и я изо всех сил пытаюсь найти правильный ответ. Если мы добавим мемоизацию для каждого селектора (при условии, что у нас их много), даже для простых геттеров, вызовет ли это проблемы с производительностью (возможно, из-за запоминания данных под капотом?)? Мемоизация для сложных селекторов, очевидно, полезна, так как предотвращает повторные вычисления, если они не нужны. Но я считаю, что мемоизация для простых селекторов также полезна, чтобы избежать бесполезного повторного рендеринга.

Фактически, я использую ловушку useSelector, и в документе говорится:

Когда действие отправлено, useSelector () выполнит эталонное сравнение предыдущего значения результата селектора и текущего значения результата. Если они разные, компонент будет вынужден повторно отрисоваться. Если они совпадают, компонент не будет повторно визуализирован.

Таким образом, даже для селектора, возвращающего одно и то же примитивное значение (скажем, int), если я не ошибаюсь, useSelector всегда должен выполнять повторную визуализацию компонента (даже если селектор всегда возвращает одно и то же значение).

Если то, что я говорю, верно, запоминание даже простых геттеров полезно в этом отношении, но может ли чрезмерное использование вызвать другие проблемы с производительностью?

Спасибо за помощь


person OTmn    schedule 04.03.2021    source источник
comment
Повторный выбор мемоизации и оптимизация производительности кода - лишь одна из причин ее использования, а лучшая производительность не гарантируется. Гарантируется, что код лучше спроектирован. Как начинающий программист, вы должны попытаться написать функции, которые реализуют что-то одно, и функции, которые должны делать более чем одно, вам нужно найти способ объединить реализацию одной функции (функции компоновки). Это то, что повторный выбор может сделать для ваших функций выбора. Такая функция, как state=>state.one.two, реализует 2 вещи: 1) расположение одного в состоянии 2) расположение двух в одном   -  person HMR    schedule 04.03.2021


Ответы (1)


Написание мемоизированных селекторных функций полезно, но многие люди пытаются запоминать слишком много селекторов, которые действительно не следует запоминать.

Если функция-селектор просто выполняет простой поиск и возврат существующих данных из состояния, нет необходимости запоминать ее - это может быть просто простая функция. Например:

const selectTodoById = (state, id) => state.todos.entities[id];

Во-вторых, вам также, вероятно, не следует создавать отдельный селектор для каждого отдельного поля состояния. Найдите разумный уровень детализации.

Мемоизированные селекторы полезны только тогда, когда селектор возвращает некоторые производные данные. Хороший пример этого - фильтрация массива:

const selectCompletedTodos = createSelector(
  state => state.todos,
  state => state.filters.completed,
  (todos, completed) => todos.filter(t => t.completed === completed)
);

Таким образом, производные данные пересчитываются только при изменении входных значений.

В качестве примечания, я на самом деле планирую написать новую страницу с основными документами Redux с инструкциями и информацией об использовании о том, когда и как писать мемоизированные селекторы. А пока вы можете прочитать мой пост Использование селекторов повторного выбора для инкапсуляции и Performance, который уже охватывает большую часть этого материала.

person markerikson    schedule 04.03.2021
comment
Я бы не предлагал использовать (state, id) => state.todos.entities[id]; не из-за запоминания, а потому, что он реализует несколько вещей. 1) расположение задач в гос. 2) расположение сущностей в задачах и 3) форма сущностей. Если вы составляете селекторы из функций, которые делают что-то одно, вам нужно изменить только эту одну функцию при изменении любой из трех упомянутых вещей. Если ваша функция выполняет более одного действия, вы, вероятно, в конечном итоге реализуете любую из трех предыдущих вещей несколько раз и должны будете изменить несколько функций, когда что-то изменится (дублирующая реализация). - person HMR; 04.03.2021
comment
Вот рефакторинг вашего примера: const selectTodos = state=>state.todos, const selectTodoEntities => createSelector([selectTodos],todos=>todos.entities) и const selectTodoById = (id) => createSelector([selectTodoEntities],entities=>entities[id]) Базовое приложение todo не показывает преимущества, но что, если селекторы реализуют сложную бизнес-логику, рассчитывающую что-то вроде стоимости выбранного страхового полиса? - person HMR; 04.03.2021
comment
Конечно, вы всегда можете абстрагироваться от локаций, если хотите. С другой стороны, в этом случае в этом действительно нет никакого преимущества, поскольку функция единственного селектора инкапсулирует как аспект местоположения, так и аспект извлечения с точки зрения вызывающего кода. . Кроме того, вы, кажется, упустили мою мысль о том, что нет никакой реальной пользы от использования Reselect для выбора элемента по регистру идентификатора, потому что нет никакого вывода - это просто прямой поиск и возврат. Итак, вы делаете этот пример более сложным, чем нужно. - person markerikson; 04.03.2021
comment
Спасибо за ваш вклад в повторный выбор, redux, react-redux и SO. Причина, по которой я поднимаю этот вопрос, заключается в том, что функция выполняет более одного действия и, вероятно, будет повторяться с помощью selectFinishedTasks, что приводит к дублированию реализации трех упомянутых вещей. Приложение todo является надуманным примером и не показывает, почему это может вызвать проблемы с обслуживанием, но если вы не укажете на это, начинающий программист не будет учиться, пока не создаст проблему, и даже не узнает, как они туда попали. как это решить. - person HMR; 04.03.2021
comment
Да, я понимаю, что нужно делать это правильно, но также важно ответить на фактический вопрос, который кто-то задает, на том уровне детализации, который они просят :) Кроме того, я часто вижу, что Reselect используется в случаях там, где этого не должно быть, это отдельная проблема, которую я хочу решить, и поэтому важно четко понимать, когда мемоизация действительно необходима. - person markerikson; 04.03.2021
comment
Привет маркериксон, спасибо за ответ. Я понимаю вашу точку зрения, но не совсем понимаю, почему. Почему не следует запоминать простые геттеры? Как я сказал во второй части своего сообщения, из-за того, что мемоизированные селекторы не пересчитываются, он возвращает ту же ссылку на значение, что позволяет избежать ненужного рендеринга компонентов React. Спасибо за ссылку. @HMR Ваша точка зрения интересна, я обязательно учту это - person OTmn; 11.03.2021
comment
@OTmn Ваш компонент должен выбирать только то, что необходимо, поэтому, если вашему компоненту требуется большая часть состояния, но он не создает новый объект, такой как const selectBigObject = state => state.bigObject, тогда ваш компонент будет повторно визуализироваться только при изменении state.bigObject, и это то, что должно произойти, в противном случае не следует выбирать весь bigObject (выберите только то, что вам нужно). Единственная причина для использования повторного выбора без вычисления - это повторное использование ранее определенной логики (см. Предложение по рефакторингу), нет вычислений, но предотвращает повторение логики (путь к элементам задач и их форма) - person HMR; 11.03.2021
comment
Мемоизация полезна только при наличии производных данных, например todos.map(t => t.text). Если вы просто возвращаете значение, например (todos, index) => todos[index], запоминать буквально нечего. - person markerikson; 11.03.2021