Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Що таке "derived state"». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Derived state** - це стан, який можна обчислити з уже наявного стану або пропсів, тобто дублікат даних, що не є "джерелом істини" (source of truth), а залежить від інших даних. **Ключове:** зберігання таких даних порушує принцип "єдиного джерела істини" і призводить до розсинхронізації, тому їх краще обчислювати на льоту або через `useMemo`.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке **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` | | Коли допустимо? | Для "знімків" даних, дебаунсу, проміжних станів | | Головний принцип | Завжди тримай "джерело істини" в одному місці |Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.