Skip to main content

Що таке "derived state"

Що таке derived state (похідний стан)

Derived state - це стан, який можна обчислити з уже наявного стану або пропсів.

Простіше кажучи: це дублікат даних, який не є "джерелом істини" (source of truth), а залежить від інших даних.


Приклад - derived state у чистому вигляді (погано)

javascript
function Example({ items }) { const [count, setCount] = useState(items.length); // derived state return <p>Елементів: {count}</p>; }

Тут count повністю залежить від items. Якщо items зміняться, count залишиться старим, доки ти не оновиш його вручну через useEffect або setCount.

Це створює несинхронність - у тебе з'являються два джерела істини (items і count).


Чому derived state небезпечний

1. Порушує принцип "Single Source of Truth"

React-архітектура будується на ідеї:

"Кожен стан повинен мати одне джерело істини."

Коли ти створюєш derived state, ти фактично робиш "копію" цих даних. Тепер потрібно стежити, щоб обидві версії завжди були синхронізовані. Це шлях до багів.


2. Несинхронні оновлення

javascript
function Example({ items }) { const [count, setCount] = useState(items.length); useEffect(() => { setCount(items.length); }, [items]); return <p>{count}</p>; }

Так, можна "синхронізувати" через useEffect, але це зайвий рендер і зайва складність.

Навіщо зберігати count, якщо його можна обчислити напряму?

Правильно:

javascript
function Example({ items }) { return <p>Елементів: {items.length}</p>; }

3. Похідні дані можуть "відставати"

Якщо ти обчислюєш derived state всередині setState, він може використовувати застарілі дані, оскільки setState асинхронний:

javascript
setItems([...items, newItem]); setCount(items.length); // ще старе значення

React застосує обидва апдейти пізніше, і count виявиться на 1 менше, ніж потрібно.


Що робити замість derived state

Замість зберігання похідних даних - обчислюй їх на льоту, коли потрібно відмалювати.


Приклад 1 - обчислення прямо в JSX

javascript
function Example({ items }) { const total = items.length; // обчислюємо без зберігання return <p>Всього елементів: {total}</p>; }

Приклад 2 - обчислення всередині useMemo (для важких обчислень)

Якщо обчислення дороге (наприклад, фільтрація, сортування), використовуй useMemo, щоб кешувати результат:

javascript
function Products({ items, filter }) { const visibleItems = useMemo( () => items.filter(item => item.category === filter), [items, filter] ); return <p>Показано: {visibleItems.length}</p>; }

Тут visibleItems - derived value, але не stored state, а просто обчислюване значення - тому безпечно.


Коли derived state все-таки допустимий

Іноді похідний стан виправданий, якщо:

  1. Він не прямо обчислюється з props/state, а зберігає проміжне значення (наприклад, користувач вводить, а ми дебаунсимо).
  2. Або тобі потрібно "заморозити" значення на момент події.

Приклади допустимих випадків:

1. Контрольований ввід (debounce)

javascript
const [query, setQuery] = useState(""); const [debouncedQuery, setDebouncedQuery] = useState(query); useEffect(() => { const id = setTimeout(() => setDebouncedQuery(query), 500); return () => clearTimeout(id); }, [query]);

debouncedQuery - derived, але свідомо і з ефектом.

2. "Знімок" даних

javascript
const [snapshot, setSnapshot] = useState(null); function handleSubmit() { setSnapshot(formData); // зберігаємо стан форми на момент відправлення }

→ Це не дублювання, а фіксація значення в часі.


Підсумок

ПитанняВідповідь
Що таке derived state?Стан, обчислюваний з іншого стану або пропсів
Чому це погано?Створює дублювання даних і розсинхронізацію
Як краще?Обчислювати на льоту або через useMemo
Коли допустимо?Для "знімків" даних, дебаунсу, проміжних станів
Головний принципЗавжди тримай "джерело істини" в одному місці

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.