useRef — е React hook, който създава мутабилен ref обект със свойство .current, запазван между рендериранията на компонента. За разлика от useState, промяната на .current не предизвиква повторно рендериране, което прави useRef идеален за съхраняване на DOM референции, таймери и всякакви стойности, които трябва да преживеят рендерирания без прерисуване на UI. Според React Documentation (2025), ref обектът се създава веднъж за времето на живот на компонента и не се променя между рендериранията, което гарантира стабилност на референцията.
Основни точки
useRef — е hook, добавен в React 16.8, който връща мутабилен ref обект с едно свойство .current. Началната стойност се присвоява на .current при монтиране на компонента. Основната разлика от обикновена променлива — ref обектът преживява повторни рендерирания: при повторно рендериране React не създава отново ref, а връща същия обект.
Вътрешната имплементация на useRef използва същия механизъм като useState и useEffect — свързан списък от hook-ове на fiber възела. Въпреки това, за разлика от useState, React не проследява промените на ref обекта, не поставя актуализации в опашка и не планира повторно рендериране. Това прави useRef изключително лек hook, който не влияе на производителността дори при чести промени.
Според 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 и props. Ref-овете са предназначени за императивни операции, които не могат да бъдат изразени декларативно: фокус, избор на текст, интеграция с библиотеки на трети страни.
Вторият по важност сценарий — съхраняване на произволни стойности, които трябва да се запазят между рендерирания, но промяната им не трябва да предизвиква повторно рендериране. Типични примери: идентификатори на таймери (setInterval/setTimeout), флагове за отмяна на заявки, предишни стойности на props за сравнение и всякакви данни, които не влияят на изхода на 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 за съхраняване на callback-ове, за да избегнат проблеми със затварянията. Например, ако useEffect се абонира за събитие и вътре в callback-а е необходимо актуално state — съхранявайте callback-а в useRef. При всяко рендериране актуализирайте ref.current с новата функция и ефектът винаги ще извиква свежия callback без повторен абонамент.
| Критерий | useRef | useState |
|---|---|---|
| Повторно рендериране | Не предизвиква при промяна | Предизвиква при всяко setState |
| Мутация | Директна: ref.current = value | Чрез setter: setState(value) |
| Използване в JSX | Не се използва (няма ефект) | Използва се в изхода на компонента |
| Пример | Таймери, DOM референции, предишни стойности | Данни от форма, UI състояние, флагове |
Съществува анти-модел: използване на useRef за съхраняване на данни, които са необходими в JSX, но промяната им не трябва да предизвиква повторно рендериране. Това води до десинхронизация — UI показва стари данни, докато ref.current вече е нов. Ако стойността се показва в UI — използвайте useState. Ако се използва само вътре в логиката — useRef.
Комбинацията useRef + useEffect — стандартен модел за проследяване на предишни стойности на props. Съхранявайте предишната стойност в 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 обектът може да се предаде като обикновен prop: <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 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също