useRef — какво е, hook за референции и работа с DOM в React

Автор: IT Sectr Публикувано: 2026-07-04 Време за четене: 9 мин

useRef — е React hook, който създава мутабилен ref обект със свойство .current, запазван между рендериранията на компонента. За разлика от useState, промяната на .current не предизвиква повторно рендериране, което прави useRef идеален за съхраняване на DOM референции, таймери и всякакви стойности, които трябва да преживеят рендерирания без прерисуване на UI. Според React Documentation (2025), ref обектът се създава веднъж за времето на живот на компонента и не се променя между рендериранията, което гарантира стабилност на референцията.

Основни точки

  • useRef — hook за създаване на мутабилен ref обект с поле .current.
  • Без повторно рендериране — промяната на .current не предизвиква прерисуване на компонента.
  • DOM референции — предава се на ref prop на елемента за директен достъп до DOM възела.
  • Съхраняване на стойности — подходящ за таймери, предишни стойности и всякакви данни между рендерирания.
  • Стабилна референция — ref обектът не се променя между рендериранията, за разлика от затварянията.

Какво е useRef в React

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.

jsx
import { useRef } from 'react';

function Component() {
    const countRef = useRef(0);
    // countRef.current = 0 първоначално
    // countRef.current = 5 след мутация
    // Няма повторно рендериране!
}

Достъп до DOM елементи чрез useRef

Най-честият сценарий за използване на useRef — получаване на директен достъп до DOM елемента. React присвоява elementRef.current референция към DOM възела след монтиране на компонента. Това е необходимо за фокусиране на input, измерване на размери на елемент, интеграция с анимационни библиотеки и библиотеки, които не използват React рендериране.

React автоматично управлява ref референциите към DOM: при монтиране присвоява действителния DOM възел, при демонтиране го нулира (задава null). Това гарантира, че .current винаги съдържа актуална стойност и предотвратява изтичане на памет чрез „висящи“ референции към изтрити елементи. Не се изисква ръчно почистване.

jsx
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 и без ненужни прерисувания.

jsx
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: кога какво да използвате

Основната дилема — изборът между useRef и useState — се решава с един въпрос: „Нужно ли е повторно рендериране при промяна на стойността?“. Ако да — useState. Ако не — useRef. useState съхранява състояние, което влияе на изхода на компонента; useRef съхранява данни, необходими за вътрешната логика, но не влияещи на UI.

На практика, разработчиците често използват useRef за съхраняване на callback-ове, за да избегнат проблеми със затварянията. Например, ако useEffect се абонира за събитие и вътре в callback-а е необходимо актуално state — съхранявайте callback-а в useRef. При всяко рендериране актуализирайте ref.current с новата функция и ефектът винаги ще извиква свежия callback без повторен абонамент.

КритерийuseRefuseState
Повторно рендериранеНе предизвиква при промянаПредизвиква при всяко setState
МутацияДиректна: ref.current = valueЧрез setter: setState(value)
Използване в JSXНе се използва (няма ефект)Използва се в изхода на компонента
ПримерТаймери, DOM референции, предишни стойностиДанни от форма, UI състояние, флагове

Съществува анти-модел: използване на useRef за съхраняване на данни, които са необходими в JSX, но промяната им не трябва да предизвиква повторно рендериране. Това води до десинхронизация — UI показва стари данни, докато ref.current вече е нов. Ако стойността се показва в UI — използвайте useState. Ако се използва само вътре в логиката — useRef.

useRef в комбинация с useEffect

Комбинацията useRef + useEffect — стандартен модел за проследяване на предишни стойности на props. Съхранявайте предишната стойност в ref, сравнете с текущата в useEffect и вземете решение на базата на разликата. Това е особено полезно при анимации, когато трябва да знаете каква стойност е била преди промяната.

jsx
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 до текущата стойност.

Типични грешки с useRef

Най-честата грешка — четене на ref.current във фазата на рендериране за изчисляване на JSX. Тъй като промяната на ref.current не предизвиква повторно рендериране, компонентът може да използва остаряла стойност. Ако стойността на .current участва в изхода на UI — използвайте useState. Ако трябва да синхронизирате ref и state, използвайте useEffect за актуализиране на state от ref.

  • Ref в JSX — четенето на ref.current във функцията за рендериране води до десинхронизация на UI и данни.
  • Ref като зависимост на useEffect — ref.current не трябва да бъде в масива от зависимости; React не проследява неговите промени.
  • Забравена null проверка — при демонтиране ref.current става null; проверявайте преди достъп.
  • Съхраняване на функции в ref — актуализирайте ref.current при всяко рендериране, ако използвате модела със свеж callback.
jsx
// ❌ Не четете 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 само за помощни стойности, които не участват в рендерирането.

Често задавани въпроси

Може ли useRef да се използва за съхраняване на предишно състояние?

Да, това е разпространен модел — previous state pattern. Създайте ref и го актуализирайте в useEffect при всяка промяна на проследяваната стойност. В момента между рендериране и ефект, ref.current съдържа предишната стойност, която може да се сравни с текущата. Това не изисква допълнителни повторни рендерирания и работи с всякакви типове данни.

Защо ref.current е null при първото рендериране?

React присвоява ref.current стойността на DOM възела едва след като елементът е рендериран и добавен към реалния DOM. По време на първото рендериране компонентът все още не е монтиран, затова ref.current е равен на началната стойност (null). Достъпът до DOM чрез ref.current е възможен само в useEffect или в обработващи събития, които се извикват след монтиране.

Каква е разликата между useRef и createRef?

createRef създава нов ref обект при всяко рендериране — трябва да се използва само в класови компоненти. useRef създава ref веднъж и връща същия обект при всички следващи рендерирания. Във функционални компоненти използвайте изключително useRef; createRef ще доведе до загуба на стойност при повторно рендериране, тъй като обектът ще бъде пресъздаден.

Може ли useRef да се предава чрез props?

Да, ref обектът може да се предаде като обикновен prop: <Child inputRef={inputRef} />. Дъщерният компонент го използва чрез inputRef.current. Въпреки това, за директно предаване на ref към DOM елемента на дъщерния компонент, използвайте forwardRef — HOC, който позволява преминаването на ref чрез props. Без forwardRef, ref няма да бъде автоматично свързан с DOM елемента вътре в дъщерния компонент.

Как да измерим размер на елемент с useRef?

Прикачете ref към елемента, след това в useEffect четете ref.current.getBoundingClientRect() или ref.current.offsetWidth / offsetHeight. За реактивно проследяване на промени в размера, използвайте ResizeObserver вътре в useEffect: създайте observer, абонирайте се за промени и актуализирайте state с новите размери. Не забравяйте да деактивирате observer-а в cleanup.

Резюме

  • useRef — hook за създаване на мутабилен ref обект, запазван между рендерирания без предизвикване на повторно рендериране.
  • DOM достъп — ref prop се предава на елемента за директен достъп до DOM възела; достъпен след монтиране.
  • Съхраняване на данни — таймери, callback-ове, флагове и всякакви стойности, които не влияят на UI, съхранявайте в ref.
  • useRef срещу useState — изберете useState, ако промяната трябва да прерисува UI; useRef — ако не.
  • Previous value pattern — съхранявайте предишната стойност в ref, актуализирайте в useEffect за сравнение.
  • Ref в JSX — не четете ref.current във фазата на рендериране; това води до десинхронизация на UI и данни.
  • forwardRef — използвайте за предаване на ref чрез props към дъщерния компонент.

Ще разработим мобилно приложение под ключ

IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.

Обсъдете проекта

Прочетете също