← Назад к вопросам

Какие знаешь способы мемоизации в Redux?

2.3 Middle🔥 241 комментариев
#State Management

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI4 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы мемоизации в Redux

Мемоизация в Redux — критически важный инструмент для оптимизации производительности React— приложений, особенно при работе с селекторами (selectors) и редюсерами. Она предотвращает ненужные перерисовки компонентов и повторные вычисления данных. Вот основные подходы:

1. Мемоизация селекторов с помощью Reselect

Библиотека Reselect — стандартный способ создания мемоизированных селекторов в Redux. Селекторы вычисляют производные данные из состояния, а Reselect кэширует результаты, пока входные параметры не изменились.

import { createSelector } from 'reselect';

// Базовые селекторы (не мемоизированные)
const getItems = state => state.shop.items;
const getTaxRate = state => state.shop.taxRate;

// Мемоизированный селектор
const getTotalPrice = createSelector(
  [getItems, getTaxRate],
  (items, taxRate) => {
    console.log('Вычисление общей стоимости...');
    const subtotal = items.reduce((sum, item) => sum + item.price, ""
    return subtotal * (1 + taxRate);
  }
);

// При многократных вызовах с одинаковым state вычисление произойдет только один раз

Ключевые особенности Reselect:

  • Простые зависимости: Селектор пересчитывается только при изменении входных параметров
  • Композируемость: Мемоизированные селекторы могут зависеть от других мемоизированных селекторов
  • Легковесность: Минимальное влияние на bundle size

2. Мемоизация в редюсерах через Immutable.js или ручную проверку

В редюсерах важно возвращать новый объект состояния только при фактическом изменении данных. Это предотвращает ложные срабатывания подписок (subscriptions).

// Без мемоизации - всегда новый объект (плохо)
function reducer(state = initialState, action) {
  switch (action.type) {
    case 'UPDATE_SETTINGS':
      return {
        ...state,
        settings: {
          ...state.settings,
          theme: action.payload.theme
        }
      };
    default:
      return state;
  }
}

// С мемоизацией - возвращаем старый объект если ничего не изменилось
function optimizedReducer(state = initialState, action) {
  switch (action.type) {
    case 'UPDATE_SETTINGS':
      if (state.settings.theme === action.payload.theme) {
        return state; // Мемоизация: избегаем ненужного обновления
      }
      return {
        ...state,
        settings: {
          ...state.settings,
          theme: action.payload.theme
        }
      };
    default:
      return state;
  }
}

3. Современные альтернативы: Redux Toolkit и Re-reselect

Redux Toolkit (RTK) включает createEntityAdapter и оптимизированные селекторы:

import { createEntityAdapter, createSelector } from '@reduxjs/toolkit';

const usersAdapter = createEntityAdapter();

// Автоматически мемоизированные селекторы
const { selectAll, selectById } = usersAdapter.getSelectors(
  state => state.users
);

// RTK также предоставляет createSelector на основе Reselect

Re-reselect расширяет Reselect для сложных случаев:

import { createCachedSelector } from 're-reselect';

const getItemById = createCachedSelector(
  [getItems, (state, itemId) => itemId],
  (items, itemId) => items.find(item => item.id === itemId)
)(
  (state, itemId) => itemId // Ключ кэша
);

4. Мемоизация компонентов через React.memo + селекторы

Мемоизация в Redux часто комбинируется с мемоизацией React-компонентов:

import React, { memo } from 'react';
import { useSelector } from 'react-redux';
import { getTotalPrice } from './selectors';

const CartTotal = memo(() => {
  const total = useSelector(getTotalPrice); // Мемоизированный селектор
  return <div>Total: ${total}</div>;
});

// Компонент перерисуется только при изменении total

5. Специализированные библиотеки: proxy-memoize

Новое поколение библиотек, таких как proxy-memoize, использует Proxy API для автоматического отслеживания зависимостей:

import { memoize } from 'proxy-memoize';

const getComplexData = memoize(state => {
  // Proxy автоматически отследит, какие свойства были использованы
  return state.data.items.filter(item => item.active).map(/* преобразование */);
});

Практические рекомендации по мемоизации в Redux

  • Начинайте с Reselect: Для большинства приложений Reselect достаточно
  • Избегайте преждевременной оптимизации: Добавляйте мемоизацию только при доказанных проблемах производительности
  • Используйте Redux Toolkit: RTK предоставляет оптимизированные инструменты "из коробки"
  • Тестируйте селекторы: Убедитесь, что мемоизация работает корректно через unit-тесты
  • Мониторьте перерисовки: Используйте React DevTools Profiler для выявления узких мест
// Пример теста мемоизированного селектора
test('селектор мемоизирует результат', () => {
  const state1 = { items: [{ price: 10 }], taxRate: 0.1 };
  const state2 = { items: [{ price: 10 }], taxRate: 0.1 };
  
  const result1 = getTotalPrice(state1);
  const result2 = getTotalPrice(state2);
  
  // При одинаковых входных данных функция вычисления не должна вызываться повторно
  // На практике это проверяется через mock функции или подсчет вызовов
});

Мемоизация в Redux — не серебряная пуля, но при грамотном применении она значительно улучшает производительность крупных приложений, уменьшая количество вычислений и перерисовок. Ключ — понимание того, что нужно мемоизировать (дорогие вычисления, часто используемые данные) и когда (при реальных, а не гипотетических проблемах с производительностью).