Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «Кастомні хуки». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**Кастомний хук** - це звичайна JS-функція, ім'я якої починається з `use`, і всередині якої використовуються інші хуки React (`useState`, `useEffect` тощо). **Ключове:** він потрібен, щоб винести і перевикористовувати логіку (стан, ефекти, підписки), не дублюючи її в компонентах.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення## Що таке кастомний хук **Кастомний хук** - це звичайна JS-функція, ім'я якої починається з `use`, і всередині якої використовуються інші хуки React (`useState`, `useEffect` тощо). Він потрібен, щоб **винести і перевикористовувати логіку** (стан, ефекти, підписки), не дублюючи її в компонентах. > Ідея: «поділися **поведінкою**, а не розміткою». Компонент лишається чистим, UI - окремо. --- ## Як зробити (мікро-приклади) #### 1) Базовий: `useToggle` ```javascript import { useState, useCallback } from "react"; export function useToggle(initial = false) { const [on, setOn] = useState(initial); const toggle = useCallback(() => setOn(v => !v), []); const setTrue = useCallback(() => setOn(true), []); const setFalse = useCallback(() => setOn(false), []); return { on, toggle, setTrue, setFalse }; } ``` #### 2) З побічним ефектом і очищенням: `useDebounce` ```javascript import { useEffect, useState } from "react"; export function useDebounce(value, delay = 300) { const [debounced, setDebounced] = useState(value); useEffect(() => { const id = setTimeout(() => setDebounced(value), delay); return () => clearTimeout(id); // cleanup }, [value, delay]); return debounced; } ``` #### 3) З локальним сховищем: `useLocalStorage` ```javascript import { useEffect, useState } from "react"; export function useLocalStorage(key, initial) { const [state, setState] = useState(() => { const raw = localStorage.getItem(key); return raw != null ? JSON.parse(raw) : initial; }); useEffect(() => { localStorage.setItem(key, JSON.stringify(state)); }, [key, state]); return [state, setState]; // знайомий інтерфейс, як у useState } ``` --- ## Правила (обмеження) хуків - must know 1. **Ім'я з** `use` Функція має називатися `useSomething`. Це не "магія", а домовленість, яку перевіряє лінтер. 2. **Викликай хуки тільки на верхньому рівні** Не можна всередині умов, циклів, вкладених функцій. Порядок викликів має бути **однаковим на кожному рендері**. ```javascript // погано if (cond) { useEffect(...); } // добре useEffect(() => { if (cond) {/*...*/} }, [cond]); ``` 3. **Викликай хуки тільки з React-функцій** З функціональних компонентів або інших хуків. Не зі звичайних функцій, не з обробників подій, не з класів. 4. **Кастомні хуки - це синхронні функції** Сам хук не має бути `async`. Асинхронність - **всередині ефектів**. 5. **Дотримуйся** `exhaustive-deps` Залежності ефектів/мемоізацій мають бути коректними. Використовуй `eslint-plugin-react-hooks` для контролю. 6. **Не змішуй відповідальність** Хук - про **одну** задачу (fetch, toggle, форми, синхронізація зі сховищем тощо). Маленькі, композиційні хуки кращі за один "гіга-хук". 7. **Стабільні посилання назовні** Якщо хук повертає функції/об'єкти, мемоізуй їх (`useCallback`/`useMemo`), щоб споживачі не ререндерилися зайвий раз. 8. **Не рендерить UI** Хук повертає **дані/методи**, а не JSX. Малювати - задача компонентів. 9. **Особливості SSR** Усе, що залежить від `window/document/localStorage`, перевіряй в ефектах або умовно (на сервері цих API немає). --- ## Коли робити кастомний хук - Повторюєш один і той самий `useEffect + useState` у кількох компонентах. - Потрібно інкапсулювати складну логіку (форми, запити, веб-сокети, синхронізація URL↔state). - Хочеш надати **чистий, тестований API** для поведінки. --- ## Рекомендації щодо API хуків - **Повертай примітивний і передбачуваний контракт** (масив, як у `useState`, або об'єкт з іменованими полями). - **Документуй залежності** (що передаємо в хук, що він повертає). - **Мінімізуй зовнішні ефекти**: усе побічне - в `useEffect` з акуратним `cleanup`.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.