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

Что такое нормализация данных в Redux?

1.7 Middle🔥 191 комментариев
#State Management

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

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

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

Что такое нормализация данных в Redux?

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

Проблемы, которые решает нормализация

Без нормализации состояние в Redux часто содержит дублированные данные и вложенные структуры, что приводит к:

  • Несогласованности: при обновлении объекта в одном месте, его копии в других частях состояния устаревают.
  • Сложности обновлений: для изменения вложенных данных требуются глубокие копии (deep copies) с использованием spread-операторов или библиотек вроде immer, что усложняет код.
  • Проблемам с производительностью: компоненты могут перерисовываться из-за изменений в несвязанных частях состояния.
  • Трудностям в кэшировании: сложно определить, есть ли объект уже в состоянии.

Принципы нормализации данных

Нормализация в Redux следует тем же принципам, что и в реляционных базах данных:

  1. Каждый тип данных хранится в своей "таблице" (отдельном слайсе или разделе состояния). Например, пользователи, посты, комментарии.
  2. Каждая запись (entity) имеет уникальный идентификатор (ID), который выступает в качестве ключа.
  3. Ссылки между записями осуществляются через эти ID, а не через вложенные объекты.
  4. Массивы ID используются для сохранения порядка записей (например, список ID постов в ленте).

Пример: денормализованные vs нормализованные данные

Денормализованный ответ API (проблемный):

// Пользователи и их посты вложены друг в друга
const denormalizedState = {
  posts: [
    {
      id: 1,
      title: 'Post 1',
      author: { id: 101, name: 'Alice' }, // Дубликат автора!
      comments: [
        { id: 1001, text: 'Cool!', user: { id: 102, name: 'Bob' } },
        { id: 1002, text: 'Nice', user: { id: 101, name: 'Alice' } } // Дубликат Alice!
      ]
    },
    {
      id: 2,
      title: 'Post 2',
      author: { id: 101, name: 'Alice' }, // Ещё один дубликат!
      comments: []
    }
  ]
}

Нормализованное состояние (рекомендуемый подход):

const normalizedState = {
  entities: {
    posts: {
      byId: {
        '1': { id: 1, title: 'Post 1', author: 101, comments: [1001, 1002] },
        '2': { id: 2, title: 'Post 2', author: 101, comments: [] }
      },
      allIds: [1, 2] // Порядок постов
    },
    users: {
      byId: {
        '101': { id: 101, name: 'Alice' }, // Одна запись на пользователя
        '102': { id: 102, name: 'Bob' }
      }
    },
    comments: {
      byId: {
        '1001': { id: 1001, text: 'Cool!', user: 102 },
        '1002': { id: 1002, text: 'Nice', user: 101 }
      }
    }
  }
}

Как работать с нормализованными данными в Redux

  1. Селекторы: используют резолвинг (denormalization на стороне чтения) для сборки нужных компонентам данных.

    // Пример селектора с помощью Reselect
    import { createSelector } from '@reduxjs/toolkit';
    
    const selectPostsState = state => state.entities.posts;
    const selectUsersState = state => state.entities.users;
    
    export const selectPostsWithAuthors = createSelector(
      [selectPostsState, selectUsersState],
      (posts, users) => {
        return posts.allIds.map(id => ({
          ...posts.byId[id],
          author: users.byId[posts.byId[id].author] // Подставляем автора по ID
        }));
      }
    );
    
  2. Редюсеры: обновляют данные идемпотентно и точечно.

    // Обновление имени пользователя становится тривиальным
    case 'USER_UPDATED':
      return {
        ...state,
        entities: {
          ...state.entities,
          users: {
            ...state.entities.users,
            byId: {
              ...state.entities.users.byId,
              [action.payload.id]: {
                ...state.entities.users.byId[action.payload.id],
                ...action.payload.changes
              }
            }
          }
        }
      };
    
  3. Библиотеки для нормализации:

    *   **Redux Toolkit**: включает `createEntityAdapter` — утилиту, которая автоматически генерирует редюсеры, селекторы и структуру `{ ids: [], entities: {} }`.
```javascript
import { createEntityAdapter } from '@reduxjs/toolkit';

const postsAdapter = createEntityAdapter();
const initialState = postsAdapter.getInitialState();

const postsSlice = createSlice({
  name: 'posts',
  initialState,
  reducers: {
    postAdded: postsAdapter.addOne,
    postUpdated: postsAdapter.updateOne,
  }
});
```
    *   **Normalizr** (от создателя Redux): библиотека для преобразования вложенных JSON в нормализованную схему.

Преимущества и недостатки

Преимущества:

  • Устранение дублирования: каждая сущность хранится один раз.
  • Эффективные обновления: изменения вносятся в одно место.
  • Предсказуемость: состояние становится проще для понимания.
  • Оптимизация производительности: через мемоизированные селекторы.

Недостатки:

  • Накладные расходы на преобразование: необходимость нормализации при получении данных и денормализации при чтении.
  • Сложность для простых случаев: для trivial -приложений может быть избыточной.
  • Когнитивная нагрузка: требуется дополнительная абстракция в коде.

Заключение

Нормализация данных в Redux — это не догма, а инструмент для управления сложностью. Для небольших приложений с простыми данными можно обойтись и без неё. Однако в средних и крупных проектах, особенно с реляционными данными, частыми обновлениями и требованиями к производительности, нормализация становится необходимым стандартом. Использование createEntityAdapter из Redux Toolkit значительно снижает порог входа и устраняет бойлерплейт, делая этот паттерн более доступным для разработчиков.

Что такое нормализация данных в Redux? | PrepBro