Нормализация данных в состоянии

Нормализация данных — один из ключевых подходов при работе с состоянием приложений на фреймворках типа Inferno. Она позволяет избежать дублирования информации, упростить обновление состояния и повысить производительность компонентов.

Принцип нормализации

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

Пример до нормализации:

const state = {
  posts: [
    {
      id: 1,
      title: "Первый пост",
      author: {
        id: 1,
        name: "Иван"
      }
    },
    {
      id: 2,
      title: "Второй пост",
      author: {
        id: 1,
        name: "Иван"
      }
    }
  ]
};

Здесь информация об авторе повторяется в каждом посте. Любое изменение имени автора потребует обхода всех постов.

После нормализации структура состояния может выглядеть так:

const state = {
  posts: {
    1: { id: 1, title: "Первый пост", authorId: 1 },
    2: { id: 2, title: "Второй пост", authorId: 1 }
  },
  authors: {
    1: { id: 1, name: "Иван" }
  }
};

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

Выгоды нормализации

  • Упрощение обновлений: изменения касаются только одного объекта в состоянии.
  • Повышение производительности: компоненты могут подписываться на отдельные сущности, минимизируя перерендеринг.
  • Чистота структуры: сложные вложенные объекты заменяются плоскими мапами, что облегчает навигацию и обработку.

Работа с нормализованным состоянием в Inferno

Inferno использует Virtual DOM и оптимизированные рендеринг-алгоритмы. При нормализованной структуре состояние можно обновлять точечно, не вызывая лишние перерендеринги. Основные подходы:

  1. Использование идентификаторов: ссылки на сущности передаются через id. Компоненты получают данные из плоских мап, что облегчает сравнение старого и нового состояния.

  2. Immutable-подход: при обновлении состояния создаются новые объекты для изменяемых сущностей, а неизменные остаются прежними. Это обеспечивает корректное определение изменений через сравнение ссылок.

  3. Селекторы: функции, которые извлекают из плоской структуры необходимые данные для компонента. Например:

function getPostWithAuthor(state, postId) {
  const post = state.posts[postId];
  const author = state.authors[post.authorId];
  return { ...post, author };
}

Селекторы скрывают внутреннюю структуру состояния, упрощая работу компонентов.

Управление отношениями между сущностями

При нормализации важно корректно моделировать связи между объектами:

  • Один к одному: хранятся через прямую ссылку id.
  • Один ко многим: массив идентификаторов у родительской сущности.
  • Многие ко многим: отдельная сущность для связи, содержащая идентификаторы обеих сторон.

Пример отношения «один ко многим»:

const state = {
  posts: {
    1: { id: 1, title: "Первый пост", commentIds: [1, 2] }
  },
  comments: {
    1: { id: 1, text: "Отлично!" },
    2: { id: 2, text: "Супер!" }
  }
};

Интеграция с состоянием компонентов

В Inferno состояние может храниться как глобально, так и локально. Для локального состояния нормализация помогает:

  • Минимизировать дублирование данных между компонентами.
  • Упростить передачу ссылок на объекты через пропсы.
  • Облегчить отладку и тестирование компонентов.

Пример локального состояния с нормализацией:

function PostsList() {
  const [state, setState] = Inferno.useState({
    posts: {
      1: { id: 1, title: "Первый пост", authorId: 1 }
    },
    authors: {
      1: { id: 1, name: "Иван" }
    }
  });

  const addPost = (post) => {
    setState(prev => ({
      ...prev,
      posts: { ...prev.posts, [post.id]: post }
    }));
  };

  const getPostWithAuthor = (postId) => {
    const post = state.posts[postId];
    const author = state.authors[post.authorId];
    return { ...post, author };
  };

  return state.posts ? (
    <ul>
      {Object.values(state.posts).map(post => (
        <li key={post.id}>
          {getPostWithAuthor(post.id).title} — {getPostWithAuthor(post.id).author.name}
        </li>
      ))}
    </ul>
  ) : null;
}

Инструменты для нормализации

  • normalizr: библиотека для автоматической нормализации вложенных данных, особенно удобна при работе с API.
  • Immutable.js или immer: помогают управлять неизменяемостью данных при обновлениях.

Рекомендации по применению

  • Нормализовать данные стоит при сложных вложенных структурах или при наличии повторяющихся объектов.
  • Для небольших, простых состояний чрезмерная нормализация усложняет код.
  • Всегда использовать селекторы и минимизировать прямые мутации глобального состояния.

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