useRef — це хук React, який створює мутований ref-об’єкт із властивістю .current, що зберігається між рендерами компонента. На відміну від useState, зміна .current не викликає ререндер, що робить useRef ідеальним для зберігання DOM-посилань, таймерів та будь-яких значень, які мають переживати рендери без перемальовування UI. За даними React Documentation (2025), ref-об’єкт створюється один раз за час життя компонента і не змінюється між рендерами, що гарантує стабільність посилання.
Головне
useRef — це хук, доданий в React 16.8, який повертає мутований ref-об’єкт з єдиною властивістю .current. Початкове значення присвоюється .current при монтуванні компонента. Головна відмінність від звичайної змінної — ref-об’єкт переживає ререндери: при повторному рендері React не перестворює ref, а повертає той самий об’єкт.
Всередині useRef використовує той самий механізм, що й useState та useEffect — зв’язаний список хуків на fiber-вузлі. Однак на відміну від useState, React не відстежує зміни ref-об’єкта, не ставить оновлення в чергу та не планує ререндер. Це робить useRef надзвичайно легким хуком, який не впливає на продуктивність навіть при частих змінах.
За даними React Team — Hooks FAQ (2025), useRef — це по суті «коробка», яка зберігає значення. Синтаксичний цукор: const ref = useRef(initialValue) еквівалентний const ref = { current: initialValue } з тією відмінністю, що React гарантує, що ref-об’єкт буде тим самим при кожному рендері. Жодної магії — просто стабільне посилання, кероване React.
import { useRef } from 'react';
function Component() {
const countRef = useRef(0);
// countRef.current = 0 спочатку
// countRef.current = 5 після мутації
// Ререндер не відбувається!
}
Найпоширеніший сценарій використання useRef — отримання прямого доступу до DOM-елемента. React присвоює elementRef.current посилання на DOM-вузол після монтування компонента. Це необхідно для фокусування input, вимірювання розмірів елемента, інтеграції з анімаційними бібліотеками та бібліотеками, які не використовують React-рендеринг.
React автоматично керує ref-посиланнями на DOM: при монтуванні присвоює актуальний DOM-вузол, при демонтуванні обнулює (встановлює null). Це гарантує, що в .current завжди актуальне значення, та запобігає витокам пам’яті через «висячі» посилання на видалені елементи. Жодного ручного очищення не потрібно.
function AutoFocusInput() {
const inputRef = useRef(null);
useEffect(() => {
// Фокусувати input після монтування компонента
inputRef.current?.focus();
}, []);
return <input ref={inputRef} type="text" />;
}
За даними React — Refs and DOM documentation (2025), не використовуй ref для декларативних операцій — відкриття/закриття модалок, керування видимістю. Для цих цілей існують state та пропси. Refs призначені для імперативних операцій, які не можна виразити декларативно: фокус, виділення тексту, інтеграція зі сторонніми бібліотеками.
Другий за важливістю сценарій — зберігання довільних значень, які мають зберігатися між рендерами, але зміна яких не повинна викликати ререндер. Типові приклади: ідентифікатори таймерів (setInterval/setTimeout), прапорці для скасування запитів, попередні значення пропсів для порівняння та будь-які дані, що не впливають на виведення UI.
Особливо це корисно в useEffect: useRef зберігає ідентифікатор таймера, а cleanup його очищає. Якщо зберігати timerId в useState, кожен виклик setTimerId викликав би зайвий ререндер, який не потрібен ні для логіки, ні для UI. useRef вирішує цю проблему без overhead та без зайвих перемальовувань.
function Timer() {
const intervalRef = useRef(null);
const start = () => {
intervalRef.current = setInterval(() => {
console.log('тік');
}, 1000);
};
const stop = () => {
clearInterval(intervalRef.current);
};
useEffect(() => stop, []);
return <><button onClick={start}>Start</button><button onClick={stop}>Stop</button></>;
}
Головна дилема — вибір між useRef та useState — вирішується одним питанням: «Чи потрібен ререндер при зміні значення?«. Якщо так — useState. Якщо ні — useRef. useState зберігає стан, що впливає на виведення компонента; useRef зберігає дані, потрібні для внутрішньої логіки, але не впливає на UI.
На практиці розробники часто використовують useRef для зберігання колбеків, щоб уникнути проблем із замиканнями. Наприклад, якщо useEffect підписується на подію, і всередині колбека потрібен актуальний state — зберігай колбек в useRef. При кожному рендері оновлюй ref.current новою функцією, і ефект буде викликати завжди свіжий колбек без перепідписки.
| Критерій | useRef | useState |
|---|---|---|
| Ререндер | Не викликає при зміні | Викликає при кожному setState |
| Мутація | Пряма: ref.current = значення | Через сетер: setState(значення) |
| Використання в JSX | Не використовується (немає ефекту) | Використовується у виведенні компонента |
| Приклад | Таймери, DOM-посилання, попередні значення | Дані форми, UI-стан, прапорці |
Існує anti-pattern: використовувати useRef для даних, які потрібні в JSX, але зміна яких не повинна викликати ререндер. Це призводить до розсинхронізації — UI показує старі дані, хоча ref.current вже новий. Якщо значення відображається в UI — використовуй useState. Якщо лише використовується всередині логіки — useRef.
Зв’язка useRef + useEffect — стандартний патерн для відстеження попередніх значень пропсів. Зберігай попереднє значення в ref, порівнюй з поточним в useEffect та приймай рішення на основі різниці. Це особливо корисно при анімаціях, коли потрібно знати, яке значення було до зміни.
function PriceDisplay({ price }) {
const prevPriceRef = useRef(price);
useEffect(() => {
const prevPrice = prevPriceRef.current;
if (price > prevPrice) {
animateUp();
} else if (price < prevPrice) {
animateDown();
}
prevPriceRef.current = price;
}, [price]);
return <span>${price}</span>;
}
За даними React Documentation — Hooks FAQ (2025), цей патерн називають «previous value pattern». Він працює, тому що useRef зберігає значення між рендерами, а useEffect виконується після фіксації змін. Спочатку оновлюється DOM з новим price, потім useEffect порівнює з попереднім (яке ще зберігається в ref) та оновлює ref.current до поточного значення.
Найчастіша помилка — читання ref.current в рендер-фазі для обчислення JSX. Оскільки зміна ref.current не викликає ререндер, компонент може використовувати застаріле значення. Якщо значення .current бере участь у виведенні UI — використовуй useState. Якщо потрібно синхронізувати ref та state, використовуй useEffect для оновлення state з ref.
// ❌ Не читай ref.current під час рендеру для відображення
function BadComponent() {
const valRef = useRef(0);
return <p>{valRef.current}</p>; // НЕ оновиться при мутації
}
// ✅ Використовуй state для відображення, ref для логіки
function GoodComponent() {
const [val, setVal] = useState(0);
const valRef = useRef(0);
return <p>{val}</p>;
}
Ще одна поширена помилка — використання useRef як єдиного способу зберігання стану в компоненті, коли дані все-таки впливають на UI. Розробники обирають useRef, щоб уникнути «зайвих» ререндерів, але в результаті отримують UI, який не оновлюється. Правильний підхід: використовуй useState для UI-даних і useRef лише для допоміжних значень, що не беруть участі в рендерингу.
Часті запитання
Так, це поширений патерн — previous state pattern. Створи ref та оновлюй його в useEffect при кожній зміні відстежуваного значення. В момент між рендером та ефектом ref.current містить попереднє значення, яке можна порівняти з поточним. Це не вимагає додаткових ререндерів і працює з будь-якими типами даних.
React присвоює ref.current значення DOM-вузла лише після того, як елемент відрендерено та додано в реальний DOM. Під час першого рендера компонент ще не змонтовано, тому ref.current дорівнює початковому значенню (null). Доступ до DOM через ref.current можливий лише в useEffect або в обробниках подій, які викликаються після монтування.
createRef створює новий ref-об’єкт при кожному рендері — його потрібно використовувати лише в класових компонентах. useRef створює ref один раз і повертає той самий об’єкт при всіх наступних рендерах. У функціональних компонентах використовуй виключно useRef; createRef призведе до втрати значення при ререндері, оскільки об’єкт буде перестворено.
Так, ref-об’єкт можна передавати як звичайний проп: <Child inputRef={inputRef} />. Дочірній компонент використовує його через inputRef.current. Однак для передачі ref безпосередньо в DOM-елемент дочірнього компонента використовуй forwardRef — HOC, який дозволяє пробросити ref через props. Без forwardRef ref не буде автоматично прив’язаний до DOM-елемента всередині дочірнього компонента.
Прив’яжи ref до елемента, потім в useEffect читай ref.current.getBoundingClientRect() або ref.current.offsetWidth / offsetHeight. Для реактивного відстеження змін розміру використовуй ResizeObserver всередині useEffect: створюй observer, підписуйся на зміни та оновлюй state з новими розмірами. Не забувай відключати observer в cleanup.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також