useEffect — то је React хук који омогућава извођење споредних ефеката у функционалним компонентама, замењујући методе животног циклуса класних компоненти: componentDidMount, componentDidUpdate и componentWillUnmount. Према React Documentation (2025), useEffect се извршава након што је React потврдио промене у DOM-у, што гарантује приступ актуелном DOM стаблу. Хук прима функцију ефекта и опциони низ зависности који управља учесталошћу извршавања.
Главно
useEffect — то је хук додат у React 16.8 за извођење споредних ефеката у функционалним компонентама. Под споредним ефектима се подразумевају операције које нису директно повезане са рендеровањем UI-ја: HTTP захтеви ка API-ју, претплате на догађаје, рад са тајмерима, манипулације DOM-ом, логирање и интеграција са библиотекама трећих страна.
Пре појаве хукова, све ове операције морале су се смештати у методе животног циклуса класних компоненти: componentDidMount за иницијализацију, componentDidUpdate за реакцију на промене props-а, componentWillUnmount за чишћење. useEffect је објединио сва три сценарија у јединственом API-ју, где низ зависности одређује када ефекат треба да се покрене. Ово је поједноставило логику и смањило дуплирање кода, посебно у сценаријима са претплатама.
Према React DevTools Usage Survey (2024), useEffect је други најпопуларнији хук после useState-а, који се користи у 89% React апликација. Већина програмера га користи за учитавање података, синхронизацију са спољним системима и управљање претплатама на DOM догађаје.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
useEffect покреће прослеђену функцију-ефекат након што је React завршио рендеровање и ажурирао DOM. Ово је кључна разлика од израчунавања током рендеровања: ефекат не блокира исцртавање, што је критично за UX перформансе. Када би се ефекти извршавали синхроно, корисник би видео «замрзнути» интерфејс током учитавања података.
Животни циклус типичног ефекта састоји се од три фазе. При монтирању компоненте, React извршава ефекат. При сваком ажурирању, ако се променила макар једна зависност из низа, React прво покреће функцију чишћења претходног ефекта, а затим — нови ефекат. При демонтирању компоненте извршава се само функција чишћења.
Према React Team — useEffect RFC (2024), интерна имплементација useEffect-а користи ред споредних ефеката у fiber стаблу. Након потврђивања промена (commit phase), React пролази кроз овај ред и позива функције ефеката редом којим су декларисане у компоненти. Сваки fiber чвор чува референцу на претходни ефекат за правилно чишћење и поновно покретање.
| Фаза | Акција React-а | Када се извршава |
|---|---|---|
| Монтирање | Позив функције ефекта | Након првог рендеровања |
| Ажурирање | cleanup → ефекат | При промени зависности |
| Демонтирање | Само cleanup | При уклањању компоненте |
Низ зависности — други аргумент useEffect-а — одређује када ефекат треба поново да се покрене. React пореди сваку вредност у низу са претходним рендеровањем помоћу Object.is. Ако се макар једна вредност променила, ефекат се поново покреће. Ако је низ празан ([]), ефекат се извршава само једном након монтирања.
Правилан избор зависности — најтежи део рада са useEffect-ом. У низ морају ући све променљиве и функције које се користе унутар ефекта и могу се мењати између рендеровања. Пропуштена зависност доводи до stale closures-а — ефекат «види» застарелу вредност из претходног рендеровања. Укључивање сувишних зависности доводи до прекомерних поновних покретања и потенцијалних грешака.
// Зависности контролишу када се ефекат поново покреће
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 ће се извршавати након сваког рендеровања. Ово може бити корисно за синхронизацију са DOM-ом или логирање, али најчешће је грешка: ефекат се извршава превише често, што доводи до губитка перформанси. У већини случајева треба проследити празан низ (једном при монтирању) или низ са конкретним props-ом/state-ом.
Празан низ ([]) значи да ефекат не зависи ни од једне вредности и извршава се строго једном. Ово је аналогно componentDidMount-у у класним компонентама. Међутим, треба запамтити: ако се унутар ефекта користе props или стање, али нису наведени у низу зависности, ефекат ће користити њихове почетне вредности и никада неће видети ажурирања. Ово се назива stale capture и често је извор тешко уочљивих грешака.
| Низ зависности | Понашање | Аналогија из класа |
|---|---|---|
| Без аргумента | Након сваког рендеровања | componentDidUpdate |
| [] | Једном при монтирању | componentDidMount |
| [a, b] | При промени a или b | Аналогија componentWillReceiveProps |
| cleanup return | Управљање демонтирањем | componentWillUnmount |
Функција чишћења (cleanup) — то је функција коју useEffect може вратити из свог callback-а. React је позива при демонтирању компоненте и пре поновног покретања ефекта при промени зависности. Cleanup је неопходан за отказивање претплата, тајмера, захтева и свих ресурса који морају бити ослобођени.
Типичан пример — претплата на WebSocket. При монтирању се ствара веза, при ажурирању зависности — поново се ствара (cleanup затвара стару, ефекат отвара нову), при демонтирању — затвара се. Без cleanup-а, свако поновно монтирање компоненте стварало би нову WebSocket везу, што би довело до цурења меморије и вишеструких конекција.
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-у да откажеш захтев.
Најчешћа грешка — пропуштање зависности. На пример, ефекат користи props userId, али је низ зависности празан. Као резултат, ефекат се извршава једном са почетном вредношћу userId-а и никада не реагује на његове промене. Програмер види да компонента добија нови userId, али подаци се не ажурирају. eslint-plugin-react-hooks са правилом exhaustive-deps открива такве грешке аутоматски.
// ❌ 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 или догађај прегледача, уверите се да се инстанца слушаоца креира једном, а не при сваком рендеровању.
Често постављана питања
Директно — не, јер useEffect очекује повратак синхроне функције или undefined. Ако је callback декларисан као async, он враћа Promise који React игнорише, а механизам чишћења престаје да ради. Решење: позовите async функцију унутар ефекта: useEffect(() => { async function load() { ... }; load(); }, []).
Нема ограничења. React препоручује раздвајање неповезане логике на засебне useEffect-е, чак и ако имају исти низ зависности. Сваки ефекат треба да буде одговоран за један јасно дефинисан споредни задатак: један за претплату, други за учитавање података, трећи за синхронизацију наслова картице. То поједностављује разумевање и отклањање грешака.
У React Strict Mode-у (само развојни режим) сви ефекти се монтирају, демонтирају и поново монтирају. Ово је функција, а не грешка — React проверава да ли чишћење ради исправно. Ако се након демонтирања и поновног монтирања ефекат понаша неправилно (на пример, претплате се дуплирају), ваше чишћење је непотпуно. У продукцији се ефекат извршава једном.
Користите 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 унутар ефекта изазвати ново рендеровање → нови ефекат → бесконачну петљу. У пракси, ефекат без низа зависности готово увек представља грешку. Изузеци — логирање или синхронизација са спољним системом где свако рендеровање захтева синхронизацију.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође