React-applicaties worden opgebouwd uit componenten, en elk component heeft een eigen levenscyclus. Het verschijnt op het scherm. Het reageert op nieuwe informatie. Uiteindelijk verdwijnt het. Negeer je dat ritme, dan is de prijs reëel. Geheugenlekken sluipen erin. De render-prestaties verslechteren. Gebruikers klikken op knoppen die niet meer reageren omdat het component dat eraan gekoppeld was tien minuten geleden is unmounted, maar de event listener nog steeds actief is. Het begrijpen van de component lifecycle is geen academische oefening. Het is de basis voor het schrijven van software die betrouwbaar blijft tijdens echt gebruik.
Zie het bestaan van een component in drie fasen. Ontwikkelaars beschrijven deze vaak als geboorte, groei en dood. React noemt ze mounting, updating en unmounting. Elke fase heeft een specifieke taak, en elke fase biedt een precies moment om code uit te voeren of op te ruimen.
De drie fasen
Mounting is de geboorte. Het component wordt aangemaakt en voor het eerst in de browser DOM geplaatst. Dit is je setup-venster. Als het component externe data nodig heeft, is dit het moment om deze op te vragen. Als het een initiële state nodig heeft die op basis van props wordt berekend, doe je dat hier. In het tijdperk van class components was dit het domein van componentDidMount. Met moderne functional components regel je dit met useEffect en een lege dependency array. Het cruciale detail is dat dit codeblok één keer wordt uitgevoerd, direct nadat React het element op de pagina heeft geplaatst. Dat maakt het ideaal voor eenmalige setup, zoals het ophalen van een gebruikersprofiel of het lezen van een waarde uit localStorage.
Updating is de groei. Een gemounte component blijft niet stilzitten. Props komen binnen van een parent. De state verandert door een gebruikersinteractie. De parent zelf re-rendert, waardoor het kind gedwongen wordt te volgen. React re-rendert vervolgens het component om de UI synchroon te houden met je data. Deze fase herhaalt zich vele malen tijdens het leven van een component. Elke keer dat een dependency verandert, worden je effects opnieuw uitgevoerd en pompt het component nieuwe output naar de DOM. Omdat deze fase zo vaak wordt getriggerd, is dit ook waar de meeste prestatieproblemen ontstaan. Onnodig werk stapelt zich hier snel op.
Unmounting is de dood. Het component verdwijnt van het scherm, misschien omdat de gebruiker naar een andere pagina is genavigeerd of omdat een conditional render is uitgeschakeld. Op dit punt wordt het component vernietigd, maar de buitenwereld weet dat niet altijd. Timers blijven lopen. WebSockets blijven luisteren. Event listeners op het window object blijven actief. Als je deze niet opruimt, houden ze referenties naar dode componenten vast en ontstaat er een geheugenlek. De browser verspilt rekenkracht aan spoken. React biedt je een cleanup function, juist zodat je intervallen kunt stoppen, verbindingen kunt sluiten en listeners kunt verwijderen voordat het component definitief verdwijnt.
Van class methods naar hooks
Jarenlang beheerden React-engineers deze fasen via class components. Logica was verdeeld over verschillende methoden. Je haalde data op binnen componentDidMount, reageerde op prop-wijzigingen binnen componentDidUpdate en ruimde de boel op binnen componentWillUnmount. Het probleem was dat gerelateerde logica versnipperd raakte. Eén enkele feature, zoals een live chat-verbinding, vereiste code in drie aparte lifecycle methods om alleen al verbinding te maken, te controleren op thread-wijzigingen en de verbinding te verbreken.
Hooks hebben dat veranderd. De useEffect hook in functional components stelt je in staat om logica te groeperen op basis van doel in plaats van op basis van de lifecycle fase. Toch sluit het mentale model naadloos aan als je weet waar je moet kijken.
Om mounting af te handelen, schrijf je useEffect(() => { ... }, []). De lege dependency array vertelt React dat dit effect geen externe dependencies heeft, waardoor het alleen na de initiële render wordt uitgevoerd. Dit vervangt componentDidMount.
Om updates af te handelen, voeg je de specifieke waarden toe die React moet volgen: useEffect(() => { ... }, [dependency]). Wanneer dependency verandert tussen renders,
