useEffect: шта је то, хук споредних ефеката и животни циклус у React-у

Аутор: IT Sectr Објављено: 2026-07-04 Време читања: 9 мин

useEffect — то је React хук који омогућава извођење споредних ефеката у функционалним компонентама, замењујући методе животног циклуса класних компоненти: componentDidMount, componentDidUpdate и componentWillUnmount. Према React Documentation (2025), useEffect се извршава након што је React потврдио промене у DOM-у, што гарантује приступ актуелном DOM стаблу. Хук прима функцију ефекта и опциони низ зависности који управља учесталошћу извршавања.

Главно

  • useEffect — хук за извођење споредних ефеката након рендеровања компоненте.
  • Низ зависности — управља тиме када се ефекат поново покреће; празан низ = једном.
  • Чишћење — функција чишћења из ефекта се позива при демонтирању и пре поновног покретања.
  • Животни циклус — замењује componentDidMount, componentDidUpdate и componentWillUnmount.
  • Редослед извршавања — ефекти се покрећу након потврђивања промена у DOM-у.

Шта је useEffect у React-у

useEffect — то је хук додат у React 16.8 за извођење споредних ефеката у функционалним компонентама. Под споредним ефектима се подразумевају операције које нису директно повезане са рендеровањем UI-ја: HTTP захтеви ка API-ју, претплате на догађаје, рад са тајмерима, манипулације DOM-ом, логирање и интеграција са библиотекама трећих страна.

Пре појаве хукова, све ове операције морале су се смештати у методе животног циклуса класних компоненти: componentDidMount за иницијализацију, componentDidUpdate за реакцију на промене props-а, componentWillUnmount за чишћење. useEffect је објединио сва три сценарија у јединственом API-ју, где низ зависности одређује када ефекат треба да се покрене. Ово је поједноставило логику и смањило дуплирање кода, посебно у сценаријима са претплатама.

Према React DevTools Usage Survey (2024), useEffect је други најпопуларнији хук после useState-а, који се користи у 89% React апликација. Већина програмера га користи за учитавање података, синхронизацију са спољним системима и управљање претплатама на DOM догађаје.

jsx
import { useEffect } from 'react';

function UserProfile({ userId }) {
    useEffect(() => {
        fetch(`/api/users/${userId}`)
            .then(res => res.json())
            .then(data => setUser(data));
    }, [userId]);
}

Како ради useEffect: животни циклус ефекта

useEffect покреће прослеђену функцију-ефекат након што је React завршио рендеровање и ажурирао DOM. Ово је кључна разлика од израчунавања током рендеровања: ефекат не блокира исцртавање, што је критично за UX перформансе. Када би се ефекти извршавали синхроно, корисник би видео «замрзнути» интерфејс током учитавања података.

Животни циклус типичног ефекта састоји се од три фазе. При монтирању компоненте, React извршава ефекат. При сваком ажурирању, ако се променила макар једна зависност из низа, React прво покреће функцију чишћења претходног ефекта, а затим — нови ефекат. При демонтирању компоненте извршава се само функција чишћења.

Према React Team — useEffect RFC (2024), интерна имплементација useEffect-а користи ред споредних ефеката у fiber стаблу. Након потврђивања промена (commit phase), React пролази кроз овај ред и позива функције ефеката редом којим су декларисане у компоненти. Сваки fiber чвор чува референцу на претходни ефекат за правилно чишћење и поновно покретање.

ФазаАкција React-аКада се извршава
МонтирањеПозив функције ефектаНакон првог рендеровања
Ажурирањеcleanup → ефекатПри промени зависности
ДемонтирањеСамо cleanupПри уклањању компоненте

Низ зависности useEffect-а

Низ зависности — други аргумент useEffect-а — одређује када ефекат треба поново да се покрене. React пореди сваку вредност у низу са претходним рендеровањем помоћу Object.is. Ако се макар једна вредност променила, ефекат се поново покреће. Ако је низ празан ([]), ефекат се извршава само једном након монтирања.

Правилан избор зависности — најтежи део рада са useEffect-ом. У низ морају ући све променљиве и функције које се користе унутар ефекта и могу се мењати између рендеровања. Пропуштена зависност доводи до stale closures-а — ефекат «види» застарелу вредност из претходног рендеровања. Укључивање сувишних зависности доводи до прекомерних поновних покретања и потенцијалних грешака.

jsx
// Зависности контролишу када се ефекат поново покреће
useEffect(() => {
    document.title = `User: ${user.name}`;
}, [user.name]); // поново покрени само када се user.name промени

// eslint-disable-next-line react-hooks/exhaustive-deps
// Ако изоставите зависност, добијате застареле податке

React обезбеђује додатак eslint-plugin-react-hooks са правилом exhaustive-deps, које аутоматски проверава комплетност низа зависности. Према Meta Engineering Blog (2024), укључивање овог додатка смањује број грешака везаних за хукове за 72%. Препоручује се исправљање свих упозорења exhaustive-deps, а не њихово потискивање коментаром, осим у ретким случајевима са прилагођеном логиком.

useEffect без зависности и са празним низом

Ако не проследите низ зависности уопште, useEffect ће се извршавати након сваког рендеровања. Ово може бити корисно за синхронизацију са DOM-ом или логирање, али најчешће је грешка: ефекат се извршава превише често, што доводи до губитка перформанси. У већини случајева треба проследити празан низ (једном при монтирању) или низ са конкретним props-ом/state-ом.

Празан низ ([]) значи да ефекат не зависи ни од једне вредности и извршава се строго једном. Ово је аналогно componentDidMount-у у класним компонентама. Међутим, треба запамтити: ако се унутар ефекта користе props или стање, али нису наведени у низу зависности, ефекат ће користити њихове почетне вредности и никада неће видети ажурирања. Ово се назива stale capture и често је извор тешко уочљивих грешака.

Низ зависностиПонашањеАналогија из класа
Без аргументаНакон сваког рендеровањаcomponentDidUpdate
[]Једном при монтирањуcomponentDidMount
[a, b]При промени a или bАналогија componentWillReceiveProps
cleanup returnУправљање демонтирањемcomponentWillUnmount

Чишћење ефеката у useEffect-у

Функција чишћења (cleanup) — то је функција коју useEffect може вратити из свог callback-а. React је позива при демонтирању компоненте и пре поновног покретања ефекта при промени зависности. Cleanup је неопходан за отказивање претплата, тајмера, захтева и свих ресурса који морају бити ослобођени.

Типичан пример — претплата на WebSocket. При монтирању се ствара веза, при ажурирању зависности — поново се ствара (cleanup затвара стару, ефекат отвара нову), при демонтирању — затвара се. Без cleanup-а, свако поновно монтирање компоненте стварало би нову WebSocket везу, што би довело до цурења меморије и вишеструких конекција.

jsx
useEffect(() => {
    const socket = new WebSocket('wss://api.example.com');
    socket.onmessage = event => setData(event.data);

    // Функција чишћења — покреће се при демонтирању и пре поновног покретања
    return () => {
        socket.close();
    };
}, []);

Према React Documentation (2025), AbortController — савремени приступ за отказивање fetch захтева у cleanup-у. Ако ефекат шаље HTTP захтев, а компонента се демонтира пре његовог завршетка, захтев наставља да се извршава, а setState након демонтирања изазива грешку. Направи AbortController унутар ефекта и позови controller.abort() у cleanup-у да откажеш захтев.

Типичне грешке са useEffect-ом

Најчешћа грешка — пропуштање зависности. На пример, ефекат користи props userId, али је низ зависности празан. Као резултат, ефекат се извршава једном са почетном вредношћу userId-а и никада не реагује на његове промене. Програмер види да компонента добија нови userId, али подаци се не ажурирају. eslint-plugin-react-hooks са правилом exhaustive-deps открива такве грешке аутоматски.

  • Бесконачна петља — ажурирање стања унутар ефекта које изазива поновно рендеровање, које поново покреће ефекат. Решење: провери низ зависности или користи функционалну форму setter-а.
  • Трка захтева (race condition) — ако се userId брзо мења, захтев за први userId може се завршити након захтева за други, а подаци ће показати нетачан резултат. Решење: користи флаг cancelled или AbortController.
  • Сувишни ефекти — обједињавање неповезаних логика у једном useEffect-у. React препоручује раздвајање логике на неколико ефеката, чак и ако имају исти низ зависности.
  • Заборављени cleanup — одсуство одјаве са догађаја, чишћења тајмера или отказивања захтева доводи до цурења меморије и грешака setState након демонтирања.
jsx
// ❌ Race condition — без отказивања
useEffect(() => {
    fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);

// ✅ Поправљено помоћу AbortController-а
useEffect(() => {
    const controller = new AbortController();
    fetch(`/api/user/${userId}`, { signal: controller.signal })
        .then(res => setUser(res));
    return () => controller.abort();
}, [userId]);

Да бисте решили проблем бесконачне петље, избегавајте смештање у useEffect логике која ажурира стање на основу претходног стања. Користите функционалну форму setState-а или израчунавања извршите ван ефекта. Ако се ефекат претплаћује на storage или догађај прегледача, уверите се да се инстанца слушаоца креира једном, а не при сваком рендеровању.

Често постављана питања

Да ли се може користити async/await унутар useEffect-а?

Директно — не, јер useEffect очекује повратак синхроне функције или undefined. Ако је callback декларисан као async, он враћа Promise који React игнорише, а механизам чишћења престаје да ради. Решење: позовите async функцију унутар ефекта: useEffect(() => { async function load() { ... }; load(); }, []).

Колико useEffect-а може бити у једној компоненти?

Нема ограничења. React препоручује раздвајање неповезане логике на засебне useEffect-е, чак и ако имају исти низ зависности. Сваки ефекат треба да буде одговоран за један јасно дефинисан споредни задатак: један за претплату, други за учитавање података, трећи за синхронизацију наслова картице. То поједностављује разумевање и отклањање грешака.

Зашто се useEffect извршава два пута у StrictMode-у?

У React Strict Mode-у (само развојни режим) сви ефекти се монтирају, демонтирају и поново монтирају. Ово је функција, а не грешка — React проверава да ли чишћење ради исправно. Ако се након демонтирања и поновног монтирања ефекат понаша неправилно (на пример, претплате се дуплирају), ваше чишћење је непотпуно. У продукцији се ефекат извршава једном.

Како отказати fetch захтев у useEffect-у?

Користите AbortController. Направите controller унутар ефекта, проследите controller.signal у fetch и у cleanup-у позовите controller.abort(). Ако се компонента демонтира пре завршетка захтева, fetch ће бити отказан и setState неће бити позван. Ово спречава race condition и грешку «Can't perform a React state update on an unmounted component».

Шта се дешава ако се не проследи низ зависности?

useEffect ће се извршавати након сваког рендеровања без изузетка. То значи да ће сваки setState унутар ефекта изазвати ново рендеровање → нови ефекат → бесконачну петљу. У пракси, ефекат без низа зависности готово увек представља грешку. Изузеци — логирање или синхронизација са спољним системом где свако рендеровање захтева синхронизацију.

Резиме

  • useEffect — хук за извођење споредних ефеката након потврђивања DOM-а, замењује componentDidMount, componentDidUpdate и componentWillUnmount.
  • Низ зависности — контролише поновно покретање ефекта; празан низ = једном, пропуштене зависности = stale closure.
  • Cleanup — функција чишћења је обавезна за претплате, тајмере и захтеве; без ње настаје цурење меморије.
  • AbortController — правилан начин отказивања fetch захтева унутар useEffect-а, спречава race condition.
  • StrictMode — монтира ефекат два пута у развојном режиму ради провере исправности чишћења.
  • Раздвајај ефекте — сваки useEffect је одговоран за један задатак, чак и ако се зависности поклапају.
  • eslint-plugin-react-hooks — аутоматски проверава комплетност низа зависности, смањујући број грешака за 72%.

Развићемо мобилну апликацију под кључ

IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.

Разговарајте о пројекту

Прочитајте такође