React is een van de populaire JavaScript bibliotheken voor het bouwen van gebruikersinterfaces. Bij het bouwen van deze interfaces kan het nodig zijn om side effects uit te voeren, zoals het fetchen van gegevens van een API, het subscriben op events of het manipuleren van de DOM.

Dat is waar de krachtige useEffect hook van pas komt. Hiermee kun je deze side effects declaratief en efficiënt afhandelen, zodat je UI responsief en up-to-date blijft.

Of je nu nieuw bent met React of een ervaren developer, het begrijpen en beheersen van useEffect is essentieel voor het maken van robuuste en dynamische applicaties. In dit artikel leer je hoe de useEffect hook werkt en hoe je hem kunt gebruiken in je React project.

Wat is een side effect in React?

Bij het werken met React componenten, zijn er momenten waarop we moeten interacten met entities of acties moeten uitvoeren buiten het bereik van React. Deze externe interacties staan bekend als side effects.

In React zijn de meeste componenten pure functies, wat betekent dat ze input ontvangen (props) en voorspelbare output produceren (JSX), zoals te zien is in het onderstaande voorbeeld:

export default function App() {
  return <User userName="JaneDoe" />   
}
  
function User(props) {
  return <h1>{props.userName}</h1>; // John Doe
}

Side effects zijn daarentegen niet voorspelbaar, omdat het interacties zijn die buiten het gebruikelijke bereik van React vallen.

Neem een voorbeeld waarin je dynamisch de titel van het browsertabblad wilt veranderen om de userName van de gebruiker weer te geven. Hoewel het verleidelijk kan zijn om dit direct binnen het component te doen, is dit niet de aanbevolen aanpak omdat dit wordt beschouwd als een side effect:

const User = ({ userName }) => {
  document.title = `Hello ${userName}`; // ❌Never do this in the component body — It is a side effect.

  return <h1>{userName}</h1>;
}

Het uitvoeren van side effects rechtstreeks binnen de component body kan het renderproces van onze React component verstoren.

Om interferentie te voorkomen, moet je side effects zo scheiden dat ze pas renderen of functioneren nadat onze component is gerenderd, zodat er een duidelijke scheiding is tussen het renderproces en alle noodzakelijke externe interacties. Deze scheiding wordt gemaakt met de useEffect hook.

De basis van useEffect begrijpen

De useEffect Hook is ontworpen om levenscyclusmethoden na te bootsen zoals componentDidMount, componentDidUpdate, en componentWillUnmount die in klassecomponenten worden gevonden.

Om useEffect te gebruiken, moet je het importeren van “react” en vervolgens callen binnen een functiecomponent (op het hoogste niveau van de component). Het heeft twee argumenten: een callback funtie en een optionele dependency array.

useEffect(callbackFn, [dependencies]);

Dit kan beter worden geschreven als:

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);
  • De callback functie bevat de code die moet worden uitgevoerd wanneer de component rendert of de dependency waarde verandert. Hier voer je de side effects uit.
  • De dependency array specificeert de waarden die moeten worden gecontroleerd op wijzigingen. De callback-functie wordt uitgevoerd als een waarde in deze array verandert.

Je kunt nu bijvoorbeeld het vorige voorbeeld corrigeren om het side effect goed uit te voeren binnen een useEffect hook:

import { useEffect } from 'react';

const User = ({ userName }) => {
  useEffect(() => {
    document.title = `Hello ${userName}`;
  }, [userName]);
    
  return <h1>{userName}</h1>;   
}

In het bovenstaande voorbeeld wordt de useEffect hook gecalld nadat het component is gerenderd en wanneer de waarde van de dependency  userName verandert.

Werken met dependencies in useEffect

Dependencies spelen een cruciale rol bij het regelen van de uitvoering van useEffect. Het is namelijk het tweede argument van de useEffect hook.

useEffect(() => {
  // code to run when the effect is triggered
}, [dependencies]);

Het gebruik van een lege dependency array [] zorgt ervoor dat het effect slechts eenmaal wordt uitgevoerd, waardoor componentDidMount wordt gesimuleerd. Het correct specificeren van dependencies zorgt ervoor dat het effect wordt bijgewerkt wanneer specifieke waarden veranderen, vergelijkbaar met componentDidUpdate.

Opmerking: Je moet voorzichtig zijn wanneer je te maken hebt met complexe dependencies . Onnodige updates kunnen worden voorkomen door zorgvuldig te kiezen welke waarden je wilt opnemen in de dependency matrix.

Als je de dependency matrix helemaal weglaat, wordt het effect elke keer uitgevoerd als het component rendert, wat kan leiden tot prestatieproblemen.

useEffect(() => {
  // code to run when the effect is triggered
});

Als je in React begrijpt hoe rendering werkt, is dat een enorm voordeel omdat je dan het belang van de dependency array kent.

Hoe werkt rendering in React?

In React genereert rendering de gebruikersinterface (UI) op basis van de huidige status en props van een component. Er zijn verschillende scenario’s waarin rendering plaatsvindt. De eerste render vindt plaats wanneer een component voor het eerst wordt gerenderd of gemount.

Daarnaast zorgt een verandering in de state of props van een component voor een re-render om ervoor te zorgen dat de UI de bijgewerkte waarden weergeeft. React applicaties worden gebouwd met een boomachtige structuur van componenten, die een hiërarchie vormen. React begint bij de hoofdcomponent tijdens het renderen en rendert recursief de kindcomponenten.

Dit betekent dat alle componenten worden gerenderd als er een wijziging optreedt in de hoofdcomponent. Het is belangrijk op te merken dat het callen van side effects (meestal dure functies) bij elke render kostbaar kan zijn. Om de prestaties te optimaliseren, kun je de dependency array in de useEffect hook gebruiken om aan te geven wanneer het moet worden geactiveerd, zodat onnodige re-renders worden beperkt.

Geavanceerd gebruik van useEffect: cleanup van side effects

De useEffect hook stelt ons in staat om side effects uit te voeren en biedt een mechanisme om die side effects op te ruimen. Dit zorgt ervoor dat resources of subscriptions die zijn aangemaakt tijdens het side effect op de juiste manier worden vrijgegeven en voorkomt geheugenlekken.

Laten we eens kijken hoe je bijwerkingen kunt opruimen met de useEffect Hook:

useEffect(() => {
  // Perform some side effect

  // Cleanup side effect
  return () => {
    // Cleanup tasks
  };
}, []);

In het bovenstaande codefragment is de cleanup functie gedefinieerd als een return waarde binnen de useEffect hook. Deze functie wordt gecalld wanneer de component wordt ontkoppeld of voordat een volgende re-render plaatsvindt. Hiermee kun je alle resources of subscriptions opruimen die zijn gemaakt tijdens het side effect.

Hier zijn enkele voorbeelden van geavanceerd gebruik van de useEffect hook voor het opruimen van side effects:

1. Intervallen opruimen

useEffect(() => {
    const interval = setInterval(() => {
        // Perform some repeated action
    }, 1000);
    return () => {
        clearInterval(interval); // Clean up the interval
    };
}, []);

In dit voorbeeld stellen we een interval in dat elke seconde een actie uitvoert. De cleanup functie verwijdert het interval om te voorkomen dat het wordt uitgevoerd nadat de component is losgekoppeld.

2. Event listeners opruimen

useEffect(() => {
    const handleClick = () => {
        // Handle the click event
    };

    window.addEventListener('click', handleClick);

    return () => {
        window.removeEventListener('click', handleClick); // Clean up the event listener
    };
}, []);

Hier maken we een event listener aan voor het click event op het window object. De cleanup functie verwijdert de event listener om geheugenlekken te voorkomen en voor een goede opruiming te zorgen.

Onthoud dat de cleanup functie optioneel is, maar het wordt sterk aangeraden om alle resources of subscriptions op te ruimen om een gezonde en efficiënte applicatie te behouden.

De useEffect hook gebruiken

De useEffect hook maakt het mogelijk om taken uit te voeren waarbij interactie is met externe entiteiten of API’s, zoals web API’s zoals localStorage of externe gegevensbronnen.

Laten we het gebruik van de useEffect hook verkennen met verschillende scenario’s:

1. Werken met web API’s (localStorage)

useEffect(() => {
 // Storing data in localStorage
  localStorage.setItem('key', 'value');
  // Retrieving data from localStorage
  const data = localStorage.getItem('key');
  // Cleanup: Clearing localStorage when component unmount
  return () => {
    localStorage.removeItem('key');
  };
}, []);

In dit voorbeeld wordt de useEffect hook gebruikt om gegevens op te slaan en op te halen uit de localStorage van de browser. De cleanup functie zorgt ervoor dat de localStorage wordt leeggemaakt als de component wordt losgekoppeld (dit is niet altijd een goed gebruik omdat je de localStorage gegevens misschien wilt bewaren totdat de browser wordt vernieuwd).

2. Gegevens fetchen van een externe API

useEffect(() => {
  // Fetching data from an external API
  fetch('https://api.example.com/data')
    .then((response) => response.json())
    .then((data) => {
      // Do something with the data
    });
}, []);

Hier wordt de useEffect hook gebruikt om gegevens op te halen van een externe API. De opgehaalde gegevens kunnen dan worden verwerkt en gebruikt in de component. Het is niet verplicht om altijd een cleanup functie toe te voegen.

Andere populaire side effects

De useEffect hook kan worden gebruikt voor verschillende andere side effects, zoals:

A. Subscriptions op events:

useEffect(() => {
  window.addEventListener('scroll', handleScroll);
  return () => {
    window.removeEventListener('scroll', handleScroll);
  };
}, []);

B. De documenttitel wijzigen:

useEffect(() => {
  document.title = 'New Title';
  return () => {
    document.title = 'Previous Title';
  };
}, []);

C. Timers beheren:

useEffect(() => {
  const timer = setInterval(() => {
    // Do something repeatedly
  }, 1000);
  return () => {
    clearInterval(timer);
  };
}, []);

Veel voorkomende useEffect fouten en hoe ze te vermijden

Tijdens het werken met de useEffect hook in React is het mogelijk om fouten tegen te komen die kunnen leiden tot onverwacht gedrag of prestatieproblemen.

Als je deze fouten begrijpt en weet hoe je ze kunt vermijden, kun je zorgen voor een soepel en foutloos gebruik van useEffect.

Laten we eens kijken naar een aantal veel voorkomende useEffect fouten en hun oplossingen:

1. Ontbrekende dependency array

Een veelgemaakte fout is het vergeten om een dependency array op te nemen als tweede argument van de useEffect hook.

ESLint zal dit altijd markeren als een waarschuwing, omdat het kan leiden tot onbedoeld gedrag, zoals overmatig opnieuw renderen of verouderde gegevens.

useEffect(() => {
  // Side effect code
}); // Missing dependency array

Oplossing: Geef altijd een afhankelijkheidsarray aan useEffect, zelfs als deze leeg is. Neem alle variabelen of waarden op waarvan het effect afhankelijk is. Dit helpt React te bepalen wanneer het effect moet worden uitgevoerd of moet worden overgeslagen.

useEffect(() => {
  // Side effect code
}, []); // Empty dependency array or with appropriate dependencies

2. Onjuiste dependency array

Ook een onjuiste dependency array kan tot problemen leiden. Als de dependency matrix niet nauwkeurig is gedefinieerd, wordt het effect mogelijk niet uitgevoerd wanneer de verwachte dependencies veranderen.

const count = 5;
const counter = 0;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Incorrect dependency array

Oplossing: Zorg ervoor dat je alle noodzakelijke dependencies opneemt in de dependency matrix. Als het effect afhankelijk is van meerdere variabelen, neem ze dan allemaal op om het effect te triggeren als een van de dependencies verandert.

const count = 5;
useEffect(() => {
  // Side effect code that depends on 'count'
  let answer = count + 15;
}, [count]); // Correct dependency array

3. Oneindige loops

Het creëren van een oneindige loop kan gebeuren als het effect een state of property wijzigt die ook afhankelijk is van het effect zelf. Dit leidt ertoe dat het effect herhaaldelijk wordt geactiveerd, wat overmatig opnieuw renderen veroorzaakt en de applicatie mogelijk bevriest.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount(count + 1); // Modifying the dependency 'count' inside the effect
}, [count]); // Dependency array includes 'count'

Oplossing: Zorg ervoor dat het effect niet direct een dependency wijzigt die is opgenomen in zijn depedency array. Maak in plaats daarvan aparte variabelen aan of gebruik andere technieken voor state beheer om noodzakelijke wijzigingen af te handelen.

const [count, setCount] = useState(0);
useEffect(() => {
  setCount((prevCount) => prevCount + 1); // Modifying the 'count' using a callback
}, []); // You can safely remove the 'count' dependency

4. Cleanup vergeten

Het vergeten om een cleanup uit te voeren op side effects kan leiden tot geheugenlekken of onnodig resourceverbruik. Het niet opruimen van event listeners, intervallen of subscriptions kan leiden tot onverwacht gedrag, vooral wanneer het component wordt ge-unmount.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  // Missing cleanup
  return () => {
    clearInterval(timer); // Cleanup missing in the return statement
  };
}, []);

Oplossing: Zorg altijd voor een cleanup functie in de return statement van de useEffect hook.

useEffect(() => {
  const timer = setInterval(() => {
    // Perform some action repeatedly
  }, 1000);
  return () => {
    clearInterval(timer); // Cleanup included in the return statement
  };
}, []);

Door je bewust te zijn van deze veelvoorkomende useEffect fouten en de aanbevolen oplossingen te volgen, kun je potentiële valkuilen vermijden en zorgen voor een correct en efficiënt gebruik van de useEffect hook in je React applicaties.

Samenvatting

React’s useEffect hook is een krachtig hulpmiddel voor het beheren van side effects in functiecomponenten. Nu je een beter begrip hebt van useEffect, is het tijd om je kennis toe te passen en je React applicaties tot leven te brengen.

Je kunt je React applicatie meteen live laten draaien door deze gratis te deployen op Kinsta’s Applicatie Hosting!

Nu is het jouw beurt. Wat is jouw mening over de useEffect hook? Deel het gerust met ons in de comments hieronder.