aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webontwikkeling

React useEffect Hook: praktische gebruiksgids

De React useEffect hook verbindt je componenten met de wereld buiten React — netwerkverzoeken, abonnementen, timers, browser-API's. Hij is krachtig, maar onzorgvuldig gebruikt levert hij oneindige lussen, geheugenlekken en moeilijk te debuggen "spook"-gedrag op. Deze gids behandelt de dependency array, cleanup-logica en de meest gemaakte fouten met echte voorbeelden.

Waar useEffect voor dient

Een effect is een neveneffect dat na de render draait. React tekent eerst het scherm en voert daarna je effect uit. Daardoor zijn effecten geschikt voor werk dat met de buitenwereld praat en de render-uitvoer niet rechtstreeks bepaalt:

  • Data ophalen van een server (fetch, axios)
  • Een WebSocket of event-abonnement opzetten
  • Een setInterval / setTimeout starten
  • De documenttitel of browser-API's bijwerken

Als je alleen de ene waarde uit een andere berekent, heb je geen effect nodig — doe dat tijdens de render. Een effect is nodig wanneer je iets buiten de render aanraakt.

De dependency array: het hart ervan

useEffect gedraagt zich op drie manieren, en het verschil zit in het tweede argument, de dependency array:

// 1) Draait na elke render
useEffect(() => {
  console.log('elke render');
});

// 2) Draait alleen bij de eerste mount
useEffect(() => {
  console.log('één keer');
}, []);

// 3) Draait telkens als userId verandert
useEffect(() => {
  fetchUser(userId);
}, [userId]);

De regel is simpel: elke reactieve waarde (prop, state, functie) die je binnen het effect gebruikt, moet in de dependency array staan. Een dependency weglaten zorgt dat het effect met verouderde (stale) waarden draait. De eslint-plugin-react-hooks van het React-team levert een exhaustive-deps-regel die dit voor je opspoort; los de onderliggende oorzaak op in plaats van de waarschuwing te onderdrukken.

De cleanup-functie en geheugenlekken

Als een effect iets blijvends opzet (een abonnement, een timer, een listener), ben jij ook verantwoordelijk voor het afbreken ervan. De functie die je vanuit het effect teruggeeft, draait voordat het component uit de DOM wordt verwijderd en voordat het effect de volgende keer opnieuw draait:

useEffect(() => {
  const id = setInterval(() => {
    setSeconds((s) => s + 1);
  }, 1000);

  return () => clearInterval(id); // cleanup
}, []);

Zonder cleanup stapelt elke heruitvoering een nieuwe setInterval op die blijft afgaan, zelfs nadat het component is gedemonteerd — een klassiek geheugenlek. Dezelfde regel geldt voor event-listeners:

useEffect(() => {
  const onResize = () => setWidth(window.innerWidth);
  window.addEventListener('resize', onResize);
  return () => window.removeEventListener('resize', onResize);
}, []);

Race conditions

Een veelvoorkomende valkuil bij het ophalen van data: klikt de gebruiker snel door records, dan kan een ouder verzoek laat binnenkomen en de nieuwere data overschrijven. Voorkom dit met een vlag (of een AbortController) in de cleanup:

useEffect(() => {
  let active = true;
  fetch(`/api/users/${userId}`)
    .then((r) => r.json())
    .then((data) => {
      if (active) setUser(data);
    });
  return () => {
    active = false; // het verouderde verzoek werkt de state niet meer bij
  };
}, [userId]);

In moderne browsers is het verzoek echt afbreken met AbortController netter. In grotere apps regelen bibliotheken zoals TanStack Query of SWR deze race conditions en caching voor je.

De meest gemaakte fouten

  • Een dependency weglaten: waarden die je uit de array laat, bevriezen (stale closure). Oplossing: volg de exhaustive-deps-regel.
  • Oneindige lus met een lege array: het echte probleem is meestal andersom — een object/array/functie die bij elke render opnieuw wordt aangemaakt in de array zetten, triggert het effect eindeloos. Stabiliseer die met useMemo / useCallback.
  • Afgeleide data in een effect zetten: gebruik geen effect om de ene state uit de andere te berekenen; bereken het tijdens de render.
  • Cleanup vergeten: schrijf een afbraak voor elk abonnement/elke timer.
  • Een async-functie direct doorgeven: useEffect(async () => …) is fout; het effect moet een cleanup-functie teruggeven, geen Promise. Definieer en roep binnenin een aparte async-functie aan.

Waarom draait StrictMode het effect twee keer?

In ontwikkeling mount React 18+ onder StrictMode elk effect bewust één keer, demonteert het direct en mount het opnieuw. Het doel is aan het licht te brengen of je cleanup juist is geschreven. Raak niet in paniek over een "bug" wanneer je effect twee keer afgaat; schrijf je de cleanup goed, dan is dit gedrag onschadelijk en gebeurt het niet in een productie-build.

Veelgestelde vragen

Wat is het verschil tussen useEffect en useLayoutEffect?

useEffect draait asynchroon nadat de browser klaar is met tekenen; in de meeste gevallen is dat de juiste keuze. useLayoutEffect draait synchroon nadat de DOM is bijgewerkt maar vóór het tekenen; gebruik het alleen wanneer je de lay-out direct moet meten en aanpassen (bijvoorbeeld een tooltip positioneren), anders kan het de prestaties schaden.

Wat gebeurt er als ik nooit een dependency array meegeef?

Laat je het tweede argument volledig weg, dan draait het effect na elke render. Dat wil je zelden en betekent meestal dat je [] of de echte dependencies had willen schrijven.

Is een effect de beste manier om data op te halen?

Voor kleine gevallen wel, maar bibliotheken zoals TanStack Query of SWR zijn voor de meeste projecten een steviger keuze omdat ze caching, retries en race conditions automatisch afhandelen.

Zijn de effecten in je React-architectuur uit de hand gelopen? Laten we je componenten doornemen en stale closures en lekken opruimen. Neem contact op en we maken je project samen robuust.

Bu kategorideki tüm yazılar →

Devamı için