Що робить «state normalization»?
Що таке нормалізація стану
State normalization - це спосіб зберігати дані у стані в «плоскій», структурованій формі, щоб:
- уникнути дублювання даних,
- спростити оновлення,
- зменшити кількість ререндерів,
- і зробити дані передбачуваними та легко кешованими.
Приклад без нормалізації (погано)
const state = {
posts: [
{
id: 1,
title: "React guide",
author: { id: 10, name: "Tim" }
},
{
id: 2,
title: "Redux patterns",
author: { id: 10, name: "Tim" } // the author is duplicated
}
]
};Тут об'єкт автора дублюється в кожному пості. Якщо автор змінить ім'я, потрібно пройтися по всіх постах і оновити копію. Крім того, кожна зміна в posts спричиняє масові ререндери.
Приклад із нормалізацією
const state = {
posts: [1, 2], // just IDs
users: { // dictionary of entities by ID
10: { id: 10, name: "Tim" }
},
entities: {
posts: {
1: { id: 1, title: "React guide", authorId: 10 },
2: { id: 2, title: "Redux patterns", authorId: 10 }
}
}
};Тепер:
- Автор зберігається в одному місці (users[10]);
- Пости посилаються на автора через authorId;
- При зміні автора не потрібно оновлювати всі пости.
Чому це важливо для продуктивності React
- Менше ререндерів
- Під час оновлення сутності React може перемалювати лише ті компоненти, які залежать від конкретного ID.
- Немає «ланцюгової» реакції через вкладені об'єкти.
- Легко порівнювати дані (shallow compare)
- Об'єкти й масиви менш вкладені -> === і React.memo працюють ефективніше.
- Спрощуються оновлення
- Зміна конкретного елемента - просто оновлення state.entities.posts[id].
- Не потрібно перебирати весь масив.
- Оптимізується кеш і селектори
- Мемоізовані селектори (наприклад, у Redux Toolkit з createSelector) працюють швидше і стабільніше.
Аналогія
Уяви, що твій state - це база даних у мініатюрі. Нормалізація перетворює його на таблиці зі зв'язками (1:1, 1:n), замість вкладених об'єктів (як «JSON у JSON»).
У React / Redux це особливо корисно
У Redux Toolkit і RTK Query це рекомендована практика. Офіційна бібліотека normalizr навіть автоматизує перетворення:
import { normalize, schema } from 'normalizr';
const user = new schema.Entity('users');
const post = new schema.Entity('posts', { author: user });
const normalizedData = normalize(originalData, [post]);Результат:
{
entities: {
users: { '10': { id: 10, name: 'Tim' } },
posts: {
'1': { id: 1, title: 'React guide', author: 10 },
'2': { id: 2, title: 'Redux patterns', author: 10 }
}
},
result: ['1', '2']
}Приклад у контексті React-компонентів
Без нормалізації:
function Post({ post }) {
return (
<div>
<h2>{post.title}</h2>
<p>{post.author.name}</p>
</div>
);
}Якщо оновиться ім'я автора, React перерендерить усі пости, тому що post.author - новий об'єкт у кожному.
З нормалізацією:
function Post({ postId }) {
const post = useSelector(state => state.entities.posts[postId]);
const author = useSelector(state => state.entities.users[post.authorId]);
return (
<div>
<h2>{post.title}</h2>
<p>{author.name}</p>
</div>
);
}Тепер оновлення author.name торкнеться лише тих компонентів, які залежать від authorId, а не всього списку постів.
Додаткові переваги нормалізації
| Перевага | Опис |
|---|---|
| Проста структура | Завжди знаєш, де шукати дані |
| Легко оновлювати і видаляти | Змінюєш за ID, без складних вкладеностей |
| Чіткі зв'язки між сутностями | «Один до багатьох», «багато до багатьох» |
| Сумісно з селекторами | Чудово працює з мемоізацією (useMemo, reselect) |
| Менше JSON parsing і diffing | Достатньо поверхневого порівняння |
Коли не варто нормалізувати
Надмірна нормалізація шкідлива, якщо:
- Даних небагато (10-20 записів, проста форма);
- Оновлення рідкісні й не зачіпають піддерева;
- Структура даних статична (наприклад, список вкладок або просте меню).
Нормалізація потрібна там, де є взаємопов'язані дані та багато оновлень.
Підсумок
| Що робить | Навіщо потрібно |
|---|---|
| Зберігає дані «плоско» (за ID) | Позбавляє від дублювання |
| Зберігає зв'язки через ID | Дозволяє оновлювати лише потрібні сутності |
| Зменшує кількість ререндерів | Підвищує продуктивність |
| Робить логіку простішою | Дані стають передбачуваними |
| Працює як міні-база даних | Легко оновлювати, шукати і кешувати |
State normalization = база даних усередині React-стану.
Одне джерело істини для кожної сутності: менше ререндерів, менше багів, вища продуктивність.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.