Найпідступніші помилки стану - неможливі комбінації, які структура даних дозволяє, а логіка - ні.
1. Суперечливі прапорці → одне поле статусу:
// погано: що означає isLoading && isError? а isSuccess && isError?
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [isSuccess, setIsSuccess] = useState(false);
// добре: рівно один стан у кожен момент
const [status, setStatus] = useState('idle'); // 'idle' | 'loading' | 'error' | 'success'
Ще краще - дискримінований тип, де дані існують лише в «правильних» станах:
type State =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'error'; error: string }
| { status: 'success'; data: Order[] };
TypeScript не дасть прочитати data в стані error.
2. Дублювання → ідентифікатори:
// погано: копія об'єкта - зміна в списку не потрапить у selectedItem
const [selectedItem, setSelectedItem] = useState(items[0]);
// добре
const [selectedId, setSelectedId] = useState(items[0].id);
const selectedItem = items.find((item) => item.id === selectedId);
3. Глибока вкладеність → нормалізація. Дерево (категорії з підкатегоріями, коментарі з відповідями) зручно відображати, але незручно оновлювати: зміна глибокого вузла вимагає копіювати весь шлях. Нормалізована форма - як таблиці в базі:
{
commentsById: {
1: { id: 1, text: '...', childIds: [2, 3] },
2: { id: 2, text: '...', childIds: [] },
},
rootIds: [1],
}
Оновлення одного коментаря - одна заміна за id. Redux Toolkit має для цього createEntityAdapter.
4. Групування пов'язаного стану: значення, що завжди змінюються разом (координати x і y, поля однієї форми), - в одному об'єкті чи редукторі, а не в окремих useState, які легко оновити неузгоджено.
5. Надлишковий стан → обчислення під час рендеру.
Перевірка структури: для кожної комбінації значень у стані запитати - чи вона можлива в реальності? Якщо ні, структуру варто змінити так, щоб її неможливо було виразити. «Зробити неможливі стани невиразними» - головна ідея.
Для складних сценаріїв (багатокрокові форми, процеси оплати, завантаження з повторами) - явний скінченний автомат: редуктор з переходами або бібліотека XState.