PAS7 Studio

Dubinski vodič za React useEffectEvent: stale closures, pretplate, listeneri, timeri i analitika u Reactu 19.2

Praktični deep dive za React useEffectEvent u Reactu 19.2: kako ispraviti stale closures, smanjiti reconnect churn u pretplatama, držati listenere i timere ažurnima te dizajnirati čišće Effects za analitiku bez ref hackova i skrivanja dependency pravila.

07. ožu 2026.· 14 min čitanja· Tehnologija
Najbolje zaFrontend inženjeriTech leadoviReact developeri koji prelaze na React 19.2Timovi koji čiste Effects prije usvajanja novijih React obrazaca
Naslovna slika s bilježnicom koja prikazuje čišćenje dependencyja prije i poslije: kaotični Effect dependencyji lijevo i čista podjela između Effect i Effect Event desno

Ovo poglavlje govori o dizajnu Effecta, a ne o trivijalnostima oko Hookova. Pitanje nije “gdje mogu koristiti useEffectEvent?”, nego “koji su dijelovi ovog Effecta stvarno reaktivni, a koji su samo reakcije na događaje kojima trebaju svježe vrijednosti?”

Mentalni model za razdvajanje jednog neurednog Effecta na granicu sinkronizacije i event-like callback. [1][2][6]
Konkretni obrasci za subscriptions, DOM listenere, timere, analitiku i custom Hooks. [1][4]
Jasno objašnjenje zašto je useEffectEvent bolji od useRef workarounda u pravim slučajevima. [1][2]
Linter-friendly pristup: kada exhaustive-deps i dalje treba voditi dizajn, a kada je dependency list postao preširok zato što kod miješa odgovornosti. [1][3]
Zanimljivi edge detalji koje timovi često propuštaju, uključujući Effect Events u custom effect hooks i novije ESLint shared settings za custom effect hooks. [1][3][7]
Checklist za nadogradnju stvarnog codebasea bez pretvaranja useEffectEvent u prečac za dependency array. [1][2][5]

Stale-closure bug je simptom koji svi prvo primijete: callback unutar Effecta čita stare vrijednosti. No dublji problem je arhitektonski. Timovi često trpaju dva različita posla u jedan Effect.

Prvi posao je sinkronizacija: spojiti se na chat room, attachati listener, pokrenuti timer, subscribati se na SDK ili inicijalizirati browser API. Drugi posao je logika odgovora: prikazati notifikaciju s aktualnom temom, uključiti najnovije analytics metapodatke, provjeriti mute flag ili formatirati podatke za trenutačnu locale. Ti poslovi stare različito i ne bi uvijek trebali reagirati na isti skup dependencyja. [1][2][5]

Prije useEffectEvent, timovi su tu napetost obično rješavali ili preširokim dependency arrayima koji su stvarali reconnect churn, ili mutable refovima koji su popravljali stale reads, ali su dizajn činili težim za razumjeti u code reviewu. useEffectEvent postoji kako bi taj razdor učinio eksplicitnim. [1][2]

React 19.2 uvodi useEffectEvent kao jednu od ključnih mogućnosti izdanja. [2]

Snimka zaslona sekcije why-this-hook-exists

Osnovna React smjernica i dalje vrijedi: Effects služe za sinkronizaciju s vanjskim sustavima, a ne za svaku promjenu statea ili user action. [5]

Snimka zaslona sekcije why-this-hook-exists

Važno preuokviravanje

Ovo nije samo alat protiv stale closures. Ovo je način da kažete: “Ovaj Effect posjeduje wiring, a ovaj ugniježđeni callback posjeduje reakciju na ono što taj wiring emitira.” [1][2]

Ako tim treba zapamtiti samo jedan dio ovog članka, neka to bude upravo ovaj.

01

Pronađite vanjski sustav

Effect bi se trebao sinkronizirati s nečim izvan Reacta: subscriptionom, timerom, DOM listenerom, browser API-jem ili SDK-om. Ako vanjski sustav ne postoji, React dokumentacija kaže da vam Effect vjerojatno uopće ne treba. [5]

02

Neka sinkronizacija ostane reaktivna

Vrijednosti koje određuju na što se povezujete, što slušate ili što raspoređujete ostaju u dependency arrayu. Ako promjena roomId, serverUrl ili element treba ponovno pokrenuti setup, to su stvarni dependencyji. [1][2]

03

Premjestite event-like logiku odgovora u useEffectEvent

Kad vanjski sustav emitira događaj, možda vam trebaju najnoviji theme, locale, plan, mute flag, analytics context ili formatting rules. Ako te vrijednosti ne bi smjele ponovno pokretati samu sinkronizaciju, ta je logika dobar kandidat za useEffectEvent. [1][2]

Jednostavan test

Pitajte se: “Ako se ova vrijednost promijeni, trebam li se reconnectati ili ponovno attachati listener?” Ako da, ostavite je kao dependency. Ako ne, ali callback i dalje treba najnoviju vrijednost kad se događaj dogodi, tada useEffectEvent počinje imati smisla. [1]

Službena React objašnjenja stalno se vraćaju primjerima chat roomova jer oni problem otkrivaju odmah. Connection treba biti reaktivan na roomId. Notifikacija i dalje treba vidjeti najnoviju theme ili drugi UI state. To su dvije različite odgovornosti.

Kada ih modelirate odvojeno, effect boundary postaje lakši za review: Effect posjeduje setup i teardown connectiona, a Effect Event posjeduje logiku što napraviti kad connection emitira događaj. [1][2]

TSX
import { useEffect, useEffectEvent } from "react";

type Props = {
  roomId: string;
  theme: "light" | "dark";
};

declare function createConnection(roomId: string): {
  on(event: "connected", cb: () => void): void;
  connect(): void;
  disconnect(): void;
};

declare function showNotification(message: string, theme: Props["theme"]): void;

export function ChatRoom({ roomId, theme }: Props) {
  const onConnected = useEffectEvent(() => {
    showNotification("Connected!", theme);
  });

  useEffect(() => {
    const connection = createConnection(roomId);
    connection.on("connected", () => {
      onConnected();
    });
    connection.connect();
    return () => connection.disconnect();
  }, [roomId]);

  return null;
}

Ovo je najčišći “aha” trenutak ovog API-ja: najnovija vrijednost i reaktivni dependency nisu uvijek ista stvar. [1][2]

Osnovni obrazac: držite connection vezanim uz roomId, a Effect Event neka čita najnovije vrijednosti bez širenja dependency boundaryja. [1][6]

Snimka zaslona sekcije canonical-chat-pattern

Zašto je ovaj primjer važan

Rješenje nije “ukloniti dependencyje”. Rješenje je prestati gurati logiku odgovora u isti reaktivni boundary koji posjeduje lifecycle connectiona. [1][2]

Subscriptions su mjesto gdje ovaj obrazac odmah donosi vrijednost. Chat, live metrics, billing streams, Firebase-like listeneri, media API-ji i third-party SDK-ovi dijele isti failure mode: setup bi trebao biti vezan uz mali skup vrijednosti, dok callback često želi širi skup “latest” UI values.

Čest smell je dependency array koji raste iz sprinta u sprint: roomId, serverUrl, theme, muted, locale, trackingContext, featureFlags, plan, segment. U tom trenutku Effect više ne opisuje external synchronization boundary; opisuje sve što je slučajno referencirano unutar ugniježđenog callbacka.

React također pokazuje da useEffectEvent posebno dobro radi unutar reusable hookova. Custom hook može omotati proslijeđeni callback u useEffectEvent, pa sam hook ostaje subscriban samo na stvarne connection inputs i ne prisiljava reconnect svaki put kad parent rerenderira s novim callback identityjem. [1][4]

TSX
import { useEffect, useEffectEvent } from "react";

type Message = { id: string; body: string };

type Options = {
  serverUrl: string;
  roomId: string;
  muted: boolean;
  locale: string;
  onReceiveMessage: (message: Message) => void;
};

declare function createConnection(options: {
  serverUrl: string;
  roomId: string;
}): {
  connect(): void;
  disconnect(): void;
  on(event: "message", cb: (message: Message) => void): void;
};

declare function playSound(name: "message"): void;
declare function formatMessagePreview(message: Message, locale: string): Message;

export function useChatRoom({
  serverUrl,
  roomId,
  muted,
  locale,
  onReceiveMessage,
}: Options): void {
  const onMessage = useEffectEvent((message: Message) => {
    const nextMessage = formatMessagePreview(message, locale);
    if (!muted) {
      playSound("message");
    }
    onReceiveMessage(nextMessage);
  });

  useEffect(() => {
    const connection = createConnection({ serverUrl, roomId });
    connection.connect();
    connection.on("message", (message) => {
      onMessage(message);
    });
    return () => connection.disconnect();
  }, [serverUrl, roomId]);
}

React vodič za custom Hooks prikazuje jedan od najkorisnijih production slučajeva: držite subscription vezan za stvarne connection inputs, a ne za callback identity. [4]

Snimka zaslona sekcije subscriptions

Dizajnerski signal

Zdrav subscription Effect ima mali dependency list koji opisuje cilj povezivanja, dok event-specifično ponašanje živi iza Effect Eventa i i dalje vidi najnovije render values. [1][4]

Analytics kod je mjesto gdje timovi najčešće žele dvije kontradiktorne stvari u isto vrijeme: stabilne subscriptions i svjež context.

Zamislite listener video playera, billing event stream, observer callback ili websocket event. Sama subscription trebala bi ovisiti o identitetu resursa: videu, računu, observer targetu ili channelu. Ali tracking payload koji se emitira trebao bi uključivati najnovije plan, experiment bucket, locale, route metadata ili naziv product surfacea. To je trenutačni context, a ne identity connectiona.

Baš je ovdje useEffectEvent lakše razumjeti od obrasca poput ref.current = latestStuff. Ref može raditi, ali skriva namjeru. Effect Event u kodu jasno kaže: “ovaj callback je dio priče Effecta, ali nije synchronization dependency.” [1][2]

TSX
import { useEffect, useEffectEvent } from "react";

type Props = {
  accountId: string;
  plan: "free" | "pro" | "team";
  experiment: string;
  routeName: string;
};

declare function subscribeToBillingEvents(
  accountId: string,
  onEvent: (eventName: string) => void,
): () => void;

declare function track(event: string, props: Record<string, string>): void;

export function BillingAnalytics({
  accountId,
  plan,
  experiment,
  routeName,
}: Props) {
  const onBillingEvent = useEffectEvent((eventName: string) => {
    track("billing_event", {
      eventName,
      plan,
      experiment,
      routeName,
    });
  });

  useEffect(() => {
    return subscribeToBillingEvents(accountId, (eventName) => {
      onBillingEvent(eventName);
    });
  }, [accountId]);

  return null;
}

Ovaj obrazac posebno dobro funkcionira u product analyticsu jer jasno odvaja “na što smo spojeni?” od “kojim podacima trebamo označiti ovaj događaj upravo sada?”.

Pravilo za analitiku

Kad tracking pokreće vanjski event source, useEffectEvent daje čišći boundary od širokih dependency arraya i workarounda s “latest values in a ref”. [1][2]

Timeri, listeneri, observeri i SDK callbackovi postavljaju isto pitanje: koje vrijednosti trebaju upravljati setupom i cleanupom, a koje samo treba pročitati kad se nešto dogodi? [1]

Comparison pointOstavite reaktivnim u EffectuPremjestite u useEffectEvent
IntervalPokretanje i čišćenje timera, plus vrijednosti poput enabled ili delay koje ga trebaju ponovno kreiratiŠto se događa na svakom ticku kada toj logici trebaju najnoviji props ili state
Window listenerAttach i remove listenera, zajedno sa stvarnim event targetom i event typeomHandler logika kojoj trebaju najnoviji filters, labels, locale ili mute flags
Observer callbackStvaranje i disconnect observera, plus njegov target i observer optionsNajnoviji analytics metadata ili UI reaction kada se callback observera pokrene
Third-party SDK subscriptionSDK inicijalizacija i teardown, plus resource identity koji definira subscriptionNajnoviji UI context ili event formatting korišten kad SDK nešto emitira

Praktično čitanje

Neka wiring pripada Effectu. Neka odgovor na događaj, koji mora ostati ažuran bez širenja synchronization boundaryja, pripada useEffectEvent. [1]

Dokumentacija sadrži nekoliko detalja koje je lako preskočiti, ali u većim codebaseovima oni su stvarno važni.

Vrijedi posebno naglasiti ovu zadnju točku, jer je useEffectEvent vrlo lako početi koristiti preširoko. On poboljšava dizajn Effecta, ali ne opravdava postojanje Effecta koji nije trebao biti napisan.

službeno podržani

React eksplicitno podržava korištenje useEffectEvent unutar vlastitih custom Hookova. To je velika stvar za reusable API-je oko subscriptions i listeners. [1][4]

držite ga lokalnim

Effect Events trebaju se pozivati samo iz Effects ili drugih Effect Events unutar iste komponente ili hooka. Nemojte ih prosljeđivati kao obične callback props. [1]

custom effect hooks

React hooks plugin dokumentira shared settings additionalEffectHooks za custom effect hooks, što timovima pomaže dosljedno lintati wrapper hookove. [3][7]

često je to najbolje rješenje

React smjernica “You Might Not Need an Effect” i dalje vrijedi. Ponekad pravi refaktor nije useEffectEvent, nego potpuno izbacivanje logike iz Effecta koji nije trebao postojati. [5]

Reference stranica jasno definira granice: Effect Events su lokalni za Effects, namjerno su isključeni iz dependency arraya i nisu general-purpose callback primitive. [1]

Snimka zaslona sekcije interesting-details

Advanced takeaway

Najzrelija upotreba useEffectEvent pojavljuje se u custom Hookovima, discipliniranim lint setupovima i code reviewu koji prvo pita treba li taj Effect uopće postojati. [1][3][4][5]

Sam Hook je malen. Većina grešaka nastaje kada ga se koristi kao prečac, a ne kao alat za dizajn.

Korištenje useEffectEvent za skrivanje vrijednosti koja bi stvarno trebala ponovno pokrenuti Effect. Ako promjena te vrijednosti treba ponovno subscribati, ponovno attachati ili reconnectati, to je i dalje dependency. [1]

Tretiranje Effect Eventa kao običnih event handlera ili njihovo prosljeđivanje kroz props. React reference je izričit: trebaju se pozivati iz Effects ili drugih Effect Events, a ne iz render logike ili proizvoljnih component boundaryja. [1]

Zadržavanje golemih Effecta i premještanje nasumičnih linija u Effect Event dok linter ne utihne. To nije refaktor, to je dependency laundering.

Preskakanje pitanja “treba li mi ovaj Effect uopće?”. React dokumentacija i dalje preporučuje brisanje nepotrebnih Effects umjesto ukrašavanja novim API-jima. [5]

Slijepa zamjena svih ref-based obrazaca. Ref i dalje ima legitimne slučajeve upotrebe; useEffectEvent je namijenjen baš event-like logici koja se pokreće iz Effecta. [1][2]

Najjednostavnija provjera razuma

Ako nakon refaktora ne možete jednom rečenicom objasniti synchronization boundary, Effect vjerojatno i dalje radi previše stvari.

Ove opcije rješavaju različite probleme. Ako ih tretirate kao međusobno zamjenjive, effect kod brzo postaje neuredan.

Koristite useEffectEvent

Najbolje radi kada vanjski sustav treba ostati sinkroniziran s uskim skupom dependencyja, ali callback koji taj sustav pokreće treba najnovije render values. Odlično za subscriptions, listenere, timere i analytics. [1][2]

Zadržite šire dependencyje

To je ispravno kada promijenjena vrijednost stvarno mijenja synchronization boundary. Reconnect nije bug ako se sama connection stvarno promijenila. [1]

Koristite ref

I dalje je valjan escape hatch za neke scenarije s mutability, ali je za event-like callback logiku obično manje jasan od Effect Eventa. Koristite ga kad je problem mutability, a ne effect event design. [1]

Izbrišite Effect

Najbolja opcija kada ne postoji vanjski sustav i logika pripada render kodu, derived stateu ili običnom event handleru. To je često i najčišći i najbrži fix. [5]

Pravo pravilo odluke

Najteže, ali i najvrjednije pitanje nije “kako da ovaj callback bude ažuran?”, nego “tko ovdje zapravo posjeduje sinkronizaciju?”. [1][2][5]

Koristite ovo tijekom React 19.2 cleanup prolaza ili pri reviewju custom Hookova koji omataju subscriptions i listenere.

Napravite audit svakog Effecta koji dotiče vanjski sustav.

Počnite sa sockets, listeners, timers, observers, media APIs i SDK wrappers. To su najvrjedniji kandidati za pregled. [5]

Zapišite stvarni synchronization boundary.

Imenujte vrijednosti koje stvarno trebaju uzrokovati novi setup i cleanup.

Odvojite “latest context” vrijednosti od “connection identity” vrijednosti.

Theme, locale, plan, experiment, mute flags, route labels i analytics metadata tipični su primjeri. [1][2]

Koristite useEffectEvent samo za event-like callback logiku.

Ako se logika ne pokreće iz Effecta ili drugog Effect Eventa, vjerojatno pripada negdje drugdje. [1]

Pregledavajte custom Hooks i ESLint postavke zajedno.

Ako tim koristi wrapper hookove oko Effecta, pregledajte konfiguraciju additionalEffectHooks u React hooks ESLint pluginu. [3][7]

Brišite Effects koji se ne sinkroniziraju s vanjskim sustavom.

Ne pretvarajte nepotrebne Effects u “pametne” Effects. Samo ih uklonite. [5]

Testirajte i listener churn i freshness callbackova.

Cilj je i manje nepotrebnih teardownova i ispravno latest-value ponašanje kada se događaj stvarno dogodi.

Očekivani rezultat

Nakon dobrog rollouta Effects postaju lakši za objasniti, lakši za lintanje i rjeđe se reconnectaju zbog nerelevantnog presentational statea.

Koji problem `useEffectEvent` zapravo rješava?

Omogućuje event-like logici koja se pokreće iz Effecta da čita najnovije props i state bez ponovne sinkronizacije cijelog okolnog Effecta. Posebno je koristan za subscriptions, listenere, timere i analytics callbackove. [1][2]

Zamjenjuje li `useEffectEvent` dependency arrays?

Ne. Vrijednosti koje stvarno definiraju synchronization boundary i dalje moraju ostati u dependency arrayu. `useEffectEvent` pomaže kada callback logika treba najnovije vrijednosti, ali te vrijednosti ne bi smjele ponovno pokretati subscription ili listener. [1]

Je li `useEffectEvent` bolji od `useRef`?

Ne uvijek. Ref i dalje ima legitimne escape-hatch slučajeve. Ali za event-like logiku koja se pokreće iz Effecta, `useEffectEvent` obično jasnije komunicira namjeru i bolje se uklapa u službeni React effect model. [1][2]

Mogu li koristiti `useEffectEvent` unutar custom Hookova?

Da. React eksplicitno dokumentira taj obrazac i to je jedan od najkorisnijih production slučajeva jer omogućuje reusable hookovima da subscriptions ostanu stabilne, a da se pritom uvijek poziva najnoviji consumer callback. [1][4]

Mogu li Effect Event proslijediti drugoj komponenti?

Ne. Effect Events trebaju se pozivati iz Effects ili drugih Effect Eventa unutar iste komponente ili hooka, a ne prosljeđivati kao obične callback props. [1]

Koja je najveća greška pri rolloutu?

Korištenje `useEffectEvent` kao prečaca za dependencyje umjesto da se pitate što stvarno definira synchronization boundary. Druga najčešća greška je da se nepotrebni Effects ne obrišu odmah na početku. [1][5]

Razumije li ESLint custom effect hooks u ovom scenariju?

Da, uz ispravnu konfiguraciju. React hooks plugin dokumentira shared settings `additionalEffectHooks` kako bi se custom effect hooks mogli dosljedno lintati. [3][7]

Uključujemo samo izvore koji izravno podupiru savjete, caveats i primjere korištene u ovom poglavlju.

Provjereno: 08. ožu 2026.Vrijedi za: React 19.2+Vrijedi za: React aplikacije sa subscriptions, listeners, timerima i analytics EffectsTestirano s: službena React 19.2 dokumentacijaTestirano s: useEffectEvent referenceTestirano s: eslint-plugin-react-hooks guidanceTestirano s: guidance za custom Hooks

Velik dio boli kod React nadogradnji nije sintaksa. To je dizajnerski dug skriven unutar Effecta: pomiješane odgovornosti, bučni dependencyji, reconnect churn i analitika zalijepljena na pogrešan sloj.

PAS7 Studio može pomoći auditirati te granice, prepoznati koje Effects treba izbrisati, a koje redizajnirati, i pretvoriti prelazak na React 19.2 u migracijski plan s pravim inženjerskim guardrailsima.

Vi ste ovdje04/04

Dubinski vodi? za useEffectEvent: dizajn Effecta, subscriptions i analitika

Prethodno
Sljedeće

Povezani članci

growth

AI SEO / GEO u 2026: vaši sljedeći kupci nisu ljudi — nego agenti

Pretraživanje se pomiče s klikova na odgovore. Botovi i AI agenti pretražuju, citiraju, preporučuju i sve češće kupuju. Saznajte što znači AI SEO / GEO, zašto klasični SEO više nije dovoljan i kako PAS7 Studio pomaže brendovima pobijediti u agentičkom webu.

blogs

Najmoćniji Apple čip? M5 Pro i M5 Max ruše rekorde

Analiza Apple M5 Pro i M5 Max čipova u ožujku 2026. Objašnjavamo zašto se ovi čipovi mogu smatrati najjačim profesionalnim laptop SoC-ovima koje je Apple dosad napravio, kako izgledaju protiv M4 Pro, M4 Max, M1 Pro, M1 Max i što pokazuju u usporedbi s aktualnim Intel i AMD konkurentima.

telegram-media-saver

Automatsko označavanje i pretraga spremljenih linkova

Integracija s GDrive/S3/Notion za automatsko označavanje i brzu pretragu putem search API-ja

services

Razvoj botova i usluge automatizacije

Profesionalni razvoj Telegram botova i automatizacija poslovnih procesa: chatbotovi, AI asistenti, CRM integracije, automatizacija radnih tijekova.

Profesionalni razvoj za vaše poslovanje

Kreiramo moderne web rješenja i botove za poduzeća. Saznajte kako vam možemo pomoći u postizanju ciljeva.