useEffect — is een React-hook waarmee je neveneffecten kunt uitvoeren in functionele componenten, ter vervanging van de levenscyclusmethoden van klassecomponenten: componentDidMount, componentDidUpdate en componentWillUnmount. Volgens React Documentation (2025) wordt useEffect uitgevoerd nadat React wijzigingen in de DOM heeft bevestigd, wat toegang tot de actuele DOM-boom garandeert. De hook accepteert een effectfunctie en een optionele afhankelijkheidslijst die de uitvoeringsfrequentie regelt.
Belangrijkste
useEffect — is een hook die in React 16.8 is toegevoegd voor het uitvoeren van neveneffecten in functionele componenten. Onder neveneffecten vallen bewerkingen die niet direct verband houden met het renderen van de UI: HTTP-verzoeken naar API's, abonneren op gebeurtenissen, werken met timers, DOM-manipulaties, loggen en integratie met bibliotheken van derden.
Vóór de komst van hooks moesten al deze bewerkingen in de levenscyclusmethoden van klassecomponenten worden geplaatst: componentDidMount voor initialisatie, componentDidUpdate voor reactie op prop-wijzigingen, componentWillUnmount voor opschonen. useEffect combineerde alle drie scenario's in één API, waarbij de afhankelijkheidslijst bepaalt wanneer het effect moet worden uitgevoerd. Dit vereenvoudigde de logica en verminderde codeduplicatie, vooral bij abonnementsscenario's.
Volgens React DevTools Usage Survey (2024) is useEffect de tweede populairste hook na useState, gebruikt in 89% van de React-applicaties. De meeste ontwikkelaars gebruiken het voor het laden van gegevens, synchronisatie met externe systemen en het beheren van DOM-gebeurtenisabonnementen.
import { useEffect } from 'react';
function UserProfile({ userId }) {
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => setUser(data));
}, [userId]);
}
useEffect voert de doorgegeven effectfunctie uit nadat React klaar is met renderen en de DOM heeft bijgewerkt. Dit is het belangrijkste verschil met berekeningen tijdens het renderen: het effect blokkeert het tekenen niet, wat cruciaal is voor UX-prestaties. Als effecten synchroon zouden worden uitgevoerd, zou de gebruiker een «bevroren» interface zien tijdens het laden van gegevens.
De levenscyclus van een typisch effect bestaat uit drie fasen. Bij het monteren van de component voert React het effect uit. Bij elke update, als ten minste één afhankelijkheid in de lijst is gewijzigd, voert React eerst de opschoningsfunctie van het vorige effect uit en daarna het nieuwe effect. Bij het ontkoppelen van de component wordt alleen de opschoningsfunctie uitgevoerd.
Volgens React Team — useEffect RFC (2024) maakt de interne implementatie van useEffect gebruik van een wachtrij voor neveneffecten in de fiber-boom. Na het bevestigen van wijzigingen (commit phase) doorloopt React deze wachtrij en roept de effectfuncties aan in de volgorde waarin ze in de component zijn gedeclareerd. Elk fiber-knooppunt slaat een verwijzing naar het vorige effect op voor correct opschonen en herstarten.
| Fase | React-actie | Wanneer uitgevoerd |
|---|---|---|
| Monteren | Aanroepen van de effectfunctie | Na eerste render |
| Bijwerken | cleanup → effect | Bij wijzigen afhankelijkheden |
| Ontkoppelen | Alleen cleanup | Bij verwijderen component |
De afhankelijkheidslijst — het tweede argument van useEffect — bepaalt wanneer het effect opnieuw moet worden gestart. React vergelijkt elke waarde in de lijst met de vorige render met behulp van Object.is. Als ten minste één waarde is gewijzigd, wordt het effect opnieuw uitgevoerd. Als de lijst leeg is ([]), wordt het effect slechts eenmaal uitgevoerd na het monteren.
De juiste keuze van afhankelijkheden — het moeilijkste deel van het werken met useEffect. In de lijst moeten alle variabelen en functies worden opgenomen die in het effect worden gebruikt en tussen renders kunnen veranderen. Het weglaten van een afhankelijkheid leidt tot stale closures — het effect «ziet» een verouderde waarde van de vorige render. Het opnemen van overbodige afhankelijkheden leidt tot overmatige heruitvoeringen en mogelijke fouten.
// Afhankelijkheden bepalen wanneer het effect opnieuw wordt uitgevoerd
useEffect(() => {
document.title = `User: ${user.name}`;
}, [user.name]); // alleen opnieuw uitvoeren wanneer user.name verandert
// eslint-disable-next-line react-hooks/exhaustive-deps
// Als je een afhankelijkheid weglaat, krijg je verouderde gegevens
React biedt de plug-in eslint-plugin-react-hooks met de regel exhaustive-deps, die automatisch de volledigheid van de afhankelijkheidslijst controleert. Volgens Meta Engineering Blog (2024) vermindert het inschakelen van deze plug-in het aantal hook-gerelateerde fouten met 72%. Het wordt aanbevolen om alle exhaustive-deps-waarschuwingen op te lossen in plaats van ze te onderdrukken met een opmerking, behalve in zeldzame gevallen met aangepaste logica.
Als je helemaal geen afhankelijkheidslijst doorgeeft, wordt useEffect na elke render uitgevoerd. Dit kan nuttig zijn voor synchronisatie met de DOM of loggen, maar is meestal een fout: het effect wordt te vaak uitgevoerd, wat leidt tot prestatieverlies. In de meeste gevallen moet je een lege lijst (eenmalig bij monteren) of een lijst met specifieke props/state doorgeven.
Een lege lijst ([]) betekent dat het effect van geen enkele waarde afhankelijk is en strikt eenmalig wordt uitgevoerd. Dit is het equivalent van componentDidMount in klassecomponenten. Houd er echter rekening mee: als er in het effect props of state worden gebruikt die niet in de afhankelijkheidslijst staan, gebruikt het effect hun beginwaarden en ziet het nooit updates. Dit wordt stale capture genoemd en is vaak de bron van moeilijk te vinden fouten.
| Afhankelijkheidslijst | Gedrag | Equivalent uit klassen |
|---|---|---|
| Zonder argument | Na elke render | componentDidUpdate |
| [] | Eenmalig bij monteren | componentDidMount |
| [a, b] | Bij wijzigen van a of b | Equivalent componentWillReceiveProps |
| cleanup return | Beheer ontkoppeling | componentWillUnmount |
De opschoningsfunctie (cleanup) — is een functie die useEffect kan retourneren uit zijn callback. React roept deze aan bij het ontkoppelen van de component en voordat het effect opnieuw wordt uitgevoerd bij wijziging van afhankelijkheden. Cleanup is noodzakelijk voor het annuleren van abonnementen, timers, verzoeken en alle bronnen die moeten worden vrijgemaakt.
Een typisch voorbeeld — een WebSocket-abonnement. Bij monteren wordt de verbinding gemaakt, bij bijwerken van afhankelijkheden — opnieuw gemaakt (cleanup sluit de oude, effect opent een nieuwe), bij ontkoppelen — gesloten. Zonder cleanup zou elke hermontage van de component een nieuwe WebSocket-verbinding creëren, wat zou leiden tot geheugenlekken en meerdere connecties.
useEffect(() => {
const socket = new WebSocket('wss://api.example.com');
socket.onmessage = event => setData(event.data);
// Opschoningsfunctie — wordt uitgevoerd bij unmount en voor heruitvoering
return () => {
socket.close();
};
}, []);
Volgens React Documentation (2025) is AbortController de moderne benadering voor het annuleren van fetch-verzoeken in cleanup. Als een effect een HTTP-verzoek doet en de component wordt ontkoppeld voordat het verzoek is voltooid, blijft het verzoek actief en veroorzaakt setState na ontkoppeling een fout. Maak een AbortController aan in het effect en roep controller.abort() aan in cleanup om het verzoek te annuleren.
De meest voorkomende fout — het weglaten van afhankelijkheden. Bijvoorbeeld, het effect gebruikt de prop userId, maar de afhankelijkheidslijst is leeg. Hierdoor wordt het effect eenmalig uitgevoerd met de beginwaarde van userId en reageert het nooit op wijzigingen. De ontwikkelaar ziet dat de component een nieuwe userId ontvangt, maar de gegevens worden niet bijgewerkt. eslint-plugin-react-hooks met de regel exhaustive-deps detecteert dergelijke fouten automatisch.
// ❌ Race condition — geen annulering
useEffect(() => {
fetch(`/api/user/${userId}`).then(res => setUser(res));
}, [userId]);
// ✅ Opgelost met AbortController
useEffect(() => {
const controller = new AbortController();
fetch(`/api/user/${userId}`, { signal: controller.signal })
.then(res => setUser(res));
return () => controller.abort();
}, [userId]);
Om het probleem van de oneindige lus op te lossen, vermijd het plaatsen van logica in useEffect die state bijwerkt op basis van vorige state. Gebruik de functionele vorm van setState of voer berekeningen buiten het effect uit. Als het effect zich abonneert op storage of een browsergebeurtenis, zorg er dan voor dat de listener-instantie eenmalig wordt gemaakt, niet bij elke render.
Veelgestelde vragen
Direct — nee, omdat useEffect verwacht dat er een synchrone functie of undefined wordt geretourneerd. Als de callback als async is gedeclareerd, retourneert deze een Promise die React negeert, waardoor het opschoningsmechanisme niet meer werkt. Oplossing: roep de async-functie aan in het effect: useEffect(() => { async function load() { ... }; load(); }, []).
Er is geen limiet. React raadt aan niet-gerelateerde logica te splitsen over afzonderlijke useEffect-hooks, zelfs als ze dezelfde afhankelijkheidslijst hebben. Elk effect moet verantwoordelijk zijn voor één duidelijk gedefinieerde neveneffecttaak: één voor abonnementen, een andere voor het laden van gegevens, een derde voor het synchroniseren van de tabbladnaam. Dit vereenvoudigt het begrip en debuggen.
In React Strict Mode (alleen ontwikkelmodus) worden alle effecten gemonteerd, ontkoppeld en opnieuw gemonteerd. Dit is een functie, geen bug — React controleert of de opschoning correct werkt. Als het effect na ontkoppeling en hermontage zich verkeerd gedraagt (bijvoorbeeld abonnementen worden gedupliceerd), is je opschoning onvolledig. In productie wordt het effect eenmalig uitgevoerd.
Gebruik AbortController. Maak een controller aan in het effect, geef controller.signal door aan fetch en roep in cleanup controller.abort() aan. Als de component wordt ontkoppeld voordat het verzoek is voltooid, wordt fetch geannuleerd en wordt setState niet aangeroepen. Dit voorkomt race conditions en de fout «Can't perform a React state update on an unmounted component».
useEffect wordt na elke render zonder uitzondering uitgevoerd. Dit betekent dat elke setState in het effect een nieuwe render → een nieuw effect → een oneindige lus veroorzaakt. In de praktijk is een effect zonder afhankelijkheidslijst bijna altijd een fout. Uitzonderingen — loggen of synchronisatie met een extern systeem waarbij elke render synchronisatie vereist.
Samenvatting
We ontwikkelen een mobiele applicatie turnkey
IT Sectr creëert sinds 2017 iOS- en Android-applicaties voor startups en bedrijven. We adviseren u en stellen de beste oplossing voor.
Lees ook