Hoe gebruik je useEffect en useState correct in React?

Laatste update: 02/12/2026
Auteur: C Bronpad
  • Begrijp hoe useState de lokale componentstatus bewaart en bijwerkt, inclusief functionele updates en objectafhandeling.
  • Gebruik useEffect voor neveneffecten met duidelijke opzet-/opruimlogica en nauwkeurige afhankelijkheidsarrays om geheugenlekken en oneindige lussen te voorkomen.
  • Combineer useState en useEffect voor praktische taken zoals het ophalen van gegevens, abonnementen en DOM-updates in functionele componenten.
  • Volg de regels van hooks en behandel effecten als processen die "na het renderen" plaatsvinden, zodat React-componenten voorspelbaar en onderhoudbaar blijven.

React-hooks useState en useEffect

React Hooks heeft de manier waarop we componenten schrijven volledig veranderd.en beheersen useState en useEffect is in feite het toegangsbewijs tot het schrijven van moderne React-code. Als je ze al gebruikt, maar nog steeds vastloopt op oneindige lussen, verouderde statussen of verwarrende afhankelijkheidsarrays, dan helpt deze handleiding je om alle ontbrekende puzzelstukjes op een praktische manier met elkaar te verbinden.

In dit artikel gaan we dieper in op hoe je het juiste gebruik kunt maken van... useState en useEffect makenWaarom hooks überhaupt zijn geïntroduceerd, de officiële regels en kanttekeningen, hoe afhankelijkheden echt werken, veelvoorkomende valkuilen die je componenten kapotmaken, en beproefde patronen voor neveneffecten, opruimen en statusbeheer in echte projecten.

Waarom hooks, en waarom specifiek useState en useEffect?

Hooks werden in React 16.8 toegevoegd, waardoor functionele componenten state- en lifecycle-functionaliteiten konden gebruiken zonder classes.Daarvoor moest je klassecomponenten schrijven om de lokale status bij te houden, je te abonneren op externe gegevens of te reageren op levenscyclusgebeurtenissen zoals het mounten en unmounten van componenten.

Het grote probleem met klassen was dat de bijbehorende logica vaak verspreid was over meerdere lifecycle-methoden. zoals componentDidMount, componentDidUpdate en componentWillUnmountJe zou uiteindelijk stukjes van dezelfde functionaliteit verspreid over verschillende methoden krijgen, gebaseerd op... wanneer ze rennen in plaats van wat Dat doen ze inderdaad, waardoor code moeilijker te lezen, te testen en te hergebruiken is.

Haakjes draaien dit model om.: met useState Je koppelt de status rechtstreeks aan een functioneel component, en met useEffect Je koppelt neveneffecten direct aan de logica die ze nodig heeft. Op die manier kun je alles wat met één specifiek probleem te maken heeft op één plek groeperen en later gemakkelijk herbruikbare hooks extraheren.

Van alle haken, useState en useEffect zijn de fundamentele basiselementenJe kunt de meeste alledaagse functies bouwen met alleen deze twee: UI-status zoals formulieren en schakelaars, netwerkverzoeken, abonnementen, timers, DOM-updates en meer. Andere hooks (useRef, useReducer, useContext, useMemo…) zijn geweldig, maar ze bouwen voort op dezelfde ideeën.

Regels voor React hooks die je nooit mag overtreden

React hooks hebben een paar strikte regels waardoor ze betrouwbaar werken bij verschillende renders.Als je ze schendt, krijg je ofwel runtimefouten ofwel zeer subtiele, moeilijk te debuggen bugs.

Eerste regel: roep hooks alleen aan binnen React-functiecomponenten of aangepaste hooks.Je kunt het niet gebruiken useState or useEffect in klassecomponenten, reguliere hulpprogrammafuncties of buiten elk component. Een patroon als dit is ongeldig:

import React, { Component, useState } from 'react';

class App extends Component {
  // ❌ This will throw - hooks don’t work in classes
  const  = useState(0);
  render() {
    return <h1>Hello, I am a Class Component!</h1>;
  }
}

De juiste aanpak is om over te stappen naar een functioneel component als je hooks wilt gebruiken.:

import React, { useState } from 'react';

function App() {
  const  = useState('');

  return (
    <div>
      Your JSX code goes in here...
    </div>
  );
}

export default App;

Tweede regel: roep hooks alleen aan op het hoogste niveau van je component.Dat betekent geen hooks binnen lussen, voorwaarden of geneste functies. React vertrouwt erop dat hooks bij elke render in dezelfde volgorde worden aangeroepen om ze op elkaar af te stemmen. useState en useEffect De oproep met de opgeslagen gegevens is ongeldig:

function BadComponent({ enabled }) {
  if (enabled) {
    // ❌ Wrong: hook inside a conditional
    const  = useState(0);
  }
  // ...
}

Declareer hooks in plaats daarvan onvoorwaardelijk bovenaan en gebruik voorwaardelijke instructies binnen het effect of de JSX.De hook moet altijd worden aangeroepen, maar de logica die hij uitvoert, kan voorwaardelijk zijn:

function ConditionalEffectComponent() {
  const  = useState(false);

  useEffect(() => {
    if (isMounted) {
      console.log('Component mounted');
    }
  }, );

  return (
    <div>
      <button onClick={() => setIsMounted(!isMounted)}>
        {isMounted ? 'Unmount' : 'Mount'}
      </button>
    </div>
  );
}

De derde impliciete regel is dat hooks moeten worden geïmporteerd vanuit React (of een hook-bibliotheek), en niet ad-hoc geïmplementeerd.Dit is vanzelfsprekend, maar toch belangrijk om te vermelden: de magie zit hem in de interne hook-dispatcher van React, die hook-aanroepen bijhoudt over alle renders heen.

Het lokale statusgebied correct beheren met useState.

useState Hiermee kun je een state koppelen aan een functioneel component en zowel de huidige waarde als een updatefunctie ontvangen.Conceptueel gezien is het de functionele tegenhanger van this.state en this.setState in klassecomponenten.

Een minimaal tegenvoorbeeld met useState het lijkt op dit:

import React, { useState } from 'react';

function Counter() {
  const  = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

export default Counter;

Wanneer je belt useState(initialValue)React slaat die status op en retourneert een paar.: de huidige statuswaarde en een setter. In tegenstelling tot normale lokale variabelen blijft de status behouden na meerdere renders, dus de count De waarde wordt niet elke keer dat de componentfunctie wordt uitgevoerd, teruggezet naar 0.

Je kunt elke serialiseerbare waarde gebruiken voor de status: getallen, tekenreeksen, booleans, arrays, objecten en zelfs functies.U kunt ook bellen useState Meerdere keren in hetzelfde component om gerelateerde waarden gescheiden te houden in plaats van alles in één object te proppen.

Als de nieuwe statuswaarde afhankelijk is van de vorige, gebruik dan altijd het functionele updateformulier.Dit voorkomt bugs die optreden wanneer meerdere statusupdates snel achter elkaar plaatsvinden:

setCount(prev => prev + 1);

Een ander subtiel maar belangrijk detail is dat het aanroepen van de setter de volledige statuswaarde vervangt; het voegt geen objecten samen zoals this.setState in de klassenAls je status een object of een array is, moet je de vorige waarde zelf verspreiden:

const  = useState({ name: 'Alex', age: 30 });

// ✅ Correct: copy and update
setUser(prev => ({ ...prev, age: prev.age + 1 }));

Voor dure beginwaarden kunt u de status lui initialiseren door een functie door te geven aan useStateReact roept deze functie alleen aan bij de eerste rendering:

const  = useState(() => calculateInitialValue());

Omgaan met bijwerkingen bij gebruikEffect

useEffect is de API van React voor het uitvoeren van neveneffecten in functionele componenten.Een "neveneffect" is alles wat de buitenwereld raakt: het ophalen van gegevens, loggen, directe DOM-wijzigingen, abonnementen, timers, browser-API's, enzovoort.

Conceptueel useEffect vervangt een combinatie van componentDidMount, componentDidUpdate en componentWillUnmount van klassecomponentenIn plaats van één effect over drie lifecycle-methoden te verdelen, declareer je het één keer en laat je React afhandelen wanneer het wordt uitgevoerd en wanneer het wordt opgeruimd.

De basishandtekening is useEffect(setup, dependencies?). De setup De functie is de body van je effect; deze kan optioneel een opruimfunctie retourneren. dependencies De array vertelt React wanneer het effect opnieuw moet worden uitgevoerd.

useEffect(() => {
  // side effect logic here

  return () => {
    // optional cleanup logic here
  };
}, );

Standaard, zonder het tweede argument, wordt het effect na elke rendering uitgevoerd. (eerste montage en elke volgende update). Dat is vaak te veel voor netwerkverzoeken of complexe logica.

Een veelvoorkomend patroon is om iets externs bij te werken telkens wanneer een stukje status verandert.Bijvoorbeeld het bijwerken van de paginatitel op basis van het aantal klikken:

import React, { useState, useEffect } from 'react';

function Counter() {
  const  = useState(0);

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  }, ); // effect re-runs only when `count` changes

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increase</button>
    </div>
  );
}

De afhankelijkheidsarray is cruciaal voor de prestaties en correctheid.Het bepaalt wanneer React het effect opnieuw moet uitvoeren: als een afhankelijkheid is gewijzigd. Object.is Bij een vergelijking wordt het effect opgeschoond en opnieuw uitgevoerd; als er niets is veranderd, wordt het overgeslagen.

De afhankelijkheidsarray als een professional begrijpen.

De afhankelijkheidsarray is waar de meest subtiele useEffect insecten komen vanReact vergelijkt elk element van de array met zijn vorige waarde. Object.isAls alle waarden gelijk zijn, wordt het effect overgeslagen; als er minstens één waarde verschilt, wordt het effect opnieuw uitgevoerd.

Er zijn drie belangrijke afhankelijkheidsconfiguraties die je constant zult gebruiken.:

  • Geen tweede argumentHet effect wordt na elke rendering uitgevoerd.
  • Lege array []Het effect wordt slechts één keer uitgevoerd bij het mounten en wordt verwijderd bij het unmounten.
  • Array met waarden Het effect treedt in werking na het laden van het systeem en telkens wanneer er een afhankelijkheid verandert.

Wanneer de afhankelijkheden primitieve waarden zijn (getallen, tekenreeksen, booleans), is dit eenvoudig.Problemen ontstaan ​​wanneer je objecten, arrays of functies in de afhankelijkheden plaatst, omdat gelijkheid gebaseerd is op referenties. Twee identieke objecten met verschillende referenties worden als "verschillend" beschouwd, wat ervoor zorgt dat de rendering bij elke keer opnieuw wordt uitgevoerd.

Beschouw een effect dat afhankelijk is van een team object uit rekwisieten:

function Team({ team }) {
  useEffect(() => {
    console.log(team.id, team.active);
  }, ); // ⚠️ might re-run every render if `team` reference changes
}

Zelfs als de daadwerkelijke teaminhoud niet verandert, zorgt een nieuwe objectverwijzing bij elke rendering ervoor dat het effect opnieuw wordt uitgevoerd.Om dit te voorkomen, kunt u ofwel afhankelijk zijn van de primitieve velden die u daadwerkelijk gebruikt, ofwel het object binnen het effect zelf reconstrueren.

Een veiligere versie registreert alleen wat het effect echt nodig heeft.:

function Team({ team }) {
  const { id, active } = team;

  useEffect(() => {
    console.log(id, active);
  }, );
}

Als je het hele object echt nodig hebt binnen het effect, kun je het daar opnieuw aanmaken in plaats van het als afhankelijkheid te gebruiken.Op die manier kan de afhankelijkheidslijst nog steeds gebaseerd zijn op primitieve gegevenstypen:

function Team({ team }) {
  const { id, active, name } = team;

  useEffect(() => {
    const localTeam = { id, active, name };
    // use `localTeam` here
  }, );
}

Als laatste redmiddel kun je memoization gebruiken met useMemo or useCallback voor dure objecten of functiesMaar onthoud dat memoïzatie zelf ook kosten met zich meebrengt. Gebruik het niet zomaar overal "voor het geval dat"; voeg het alleen toe wanneer een specifieke afhankelijkheid daadwerkelijk prestatieproblemen veroorzaakt.

Effecten correct opruimen

Sommige bijwerkingen leggen beslag op hulpbronnen die moeten worden vrijgegeven.Denk hierbij aan abonnementen, sockets, intervallen, time-outs, gebeurtenislisteners, enzovoort. Als je vergeet deze op te ruimen, kan dat gemakkelijk leiden tot geheugenlekken of dubbel werk.

In useEffectDe opruiming wordt afgehandeld door een functie terug te geven vanuit het effect.React roept deze functie aan voordat het effect opnieuw wordt uitgevoerd met nieuwe afhankelijkheden, en ook nog een laatste keer wanneer de component wordt verwijderd.

import { useEffect } from 'react';

function LogMessage({ message }) {
  useEffect(() => {
    const log = setInterval(() => {
      console.log(message);
    }, 1000);

    return () => {
      clearInterval(log);
    };
  }, );

  return <div>logging to console "{message}"</div>;
}

In dit voorbeeld, elke keer message Bij wijzigingen wist React eerst het oude interval en stelt vervolgens een nieuw interval in met het bijgewerkte bericht.Wanneer het component uit de gebruikersinterface verdwijnt, wist de laatste opruimactie het interval definitief.

Deze combinatie van "voorbereiding + opruimen" staat centraal in het mentale model van useEffectProbeer elk effect te zien als een op zichzelf staand proces dat begint in de setup-functie en volledig stopt in de cleanup-functie. React kan tijdens de ontwikkeling meerdere setup/cleanup-cycli uitvoeren (vooral in de strikte modus) om te testen of je cleanup-functie echt alles ongedaan maakt.

Een klassiek voorbeeld is het abonneren op een externe bron, zoals een chat-API of een browsergebeurtenis (zie Het afhandelen van onKeyDown in React):

useEffect(() => {
  function handleClick(event) {
    console.log('Clicked', event.clientX, event.clientY);
  }

  document.addEventListener('click', handleClick);

  return () => {
    document.removeEventListener('click', handleClick);
  };
}, []); // runs once on mount, cleans up on unmount

Het samen gebruiken van useState en useEffect voor het ophalen van gegevens.

Een van de meest voorkomende combinaties in de praktijk is het gebruik van useState en useEffect om gegevens op te halen van een APIJe bewaart de data (en eventueel laad-/foutmeldingen) in de state en voert het verzoek uit in een effect dat wordt uitgevoerd wanneer de component wordt geladen of wanneer een parameter verandert.

Een basispatroon voor het ophalen van gegevens na het mounten ziet er als volgt uit::

import { useEffect, useState } from 'react';

function FetchItems() {
  const  = useState([]);

  useEffect(() => {
    let ignore = false;

    async function fetchItems() {
      try {
        const response = await fetch('/items');
        const fetchedItems = await response.json();
        if (!ignore) {
          setItems(fetchedItems);
        }
      } catch (error) {
        console.error('Error fetching items:', error);
      }
    }

    fetchItems();

    return () => {
      // avoid updating state if the component unmounted
      ignore = true;
    };
  }, []);

  return (
    <div>
      {items.map(item => (
        <div key={item.id ?? item}>{item.name ?? item}</div>
      ))}
    </div>
  );
}

Hier zorgt de lege afhankelijkheidsarray ervoor dat het verzoek precies één keer wordt uitgevoerd.. De interne ignore Een vlag is een eenvoudige manier om te voorkomen dat de status van een niet-gemonteerde component wordt ingesteld voor het geval het verzoek te laat wordt afgehandeld.

Het is ook heel gebruikelijk om een ​​laadindicator toe te voegen en een spinner of placeholder weer te geven terwijl de gegevens worden geladen.:

const Statistics = () => {
  const  = useState([]);
  const  = useState(true);

  useEffect(() => {
    const getStats = async () => {
      try {
        const statsData = await getData();
        setStats(statsData);
      } finally {
        setLoading(false);
      }
    };

    getStats();
  }, []);

  if (loading) {
    return <div>Loading statistics...</div>;
  }

  return (
    <ul>
      {stats.map(stat => (
        <li key={stat.id}>{stat.label}: {stat.value}</li>
      ))}
    </ul>
  );
};

Als uw query afhankelijk is van een parameter (zoals een categorie, filter of routeparameter), voeg die parameter dan toe aan de afhankelijkheidsarray. Het effect treedt dus opnieuw op wanneer het verandert:

useEffect(() => {
  async function fetchItems() {
    const response = await fetch(`/items?category=${category}`);
    const data = await response.json();
    setItems(data);
  }

  fetchItems();
}, );

Denken in termen van "effecten bij elke rendering" versus "levenscycli".

Als je gewend bent aan klassecomponenten, kan het verleidelijk zijn om mentaal een kaart te maken. useEffect naar mount/update/unmount-methodenMaar dat leidt meestal tot meer verwarring. Een eenvoudiger mentaal model is: "effecten worden uitgevoerd na het renderen en kunnen worden opgeruimd voordat de volgende run begint".

In de lessen moest je vaak logica dupliceren tussen verschillende onderdelen. componentDidMount en componentDidUpdate Omdat je wilde dat hetzelfde effect zowel bij het mounten als bij updates zou worden uitgevoerd. Met hooks verdwijnt die duplicatie: één enkel effect dekt beide gevallen, en React zorgt voor het opruimen tussen de uitvoeringen.

Dit ontwerp elimineert tevens een hele categorie bugs die te maken hebben met het niet correct verwerken van updates.Bijvoorbeeld, in een klassecomponent die zich abonneert op de online status van een vriend, is het gemakkelijk om te vergeten je opnieuw te abonneren wanneer... props.friend wijzigingen, wat leidt tot verouderde abonnementen of crashes bij het ontkoppelen. Met useEffect die lijst friend.id Als afhankelijkheid zal React automatisch de opruimacties voor de oude afhankelijkheid uitvoeren en de configuratie voor de nieuwe afhankelijkheid verzorgen.

Houd er rekening mee dat React in de ontwikkelingsmodus (Strict Mode) de setup- en cleanup-cyclus opzettelijk twee keer uitvoert bij het mounten.Dit gebeurt niet in een productieomgeving, maar het is een nuttige stresstest om te bevestigen dat je opruimactie echt alles ongedaan maakt en dat je effect veilig meerdere keren kan worden uitgevoerd.

Het optimaliseren en oplossen van problemen met useEffect-gedrag

Als een effect vaker wordt uitgevoerd dan je verwacht, is het eerste wat je moet controleren de afhankelijkheidsarray.Ofwel verandert een afhankelijkheid bij elke rendering (wat vaak voorkomt bij inline objecten/functies), ofwel bent u vergeten de array überhaupt te specificeren.

Het loggen van de afhankelijkheidswaarden is een snelle manier om fouten op te sporen.:

useEffect(() => {
  console.log('Effect deps:', dep1, dep2);
}, );

Als je elke keer andere logbestanden ziet, onderzoek dan welke afhankelijkheid er daadwerkelijk verandert.Vaak zie je dat een inline object of pijlfunctie bij elke rendering opnieuw wordt aangemaakt. Het verplaatsen van objectcreatie naar binnen het effect, het verplaatsen van functies naar buiten de component, of het memoïzeren ervan met useCallback Kan afhankelijkheden stabiliseren wanneer dat nodig is.

Oneindige lussen ontstaan ​​wanneer een effect afhankelijk is van een waarde en diezelfde waarde onvoorwaardelijk bijwerkt.. Bijvoorbeeld:

useEffect(() => {
  setCount(count + 1); // ⚠️ will cause a loop if `count` is a dependency
}, );

Elke keer weer count wijzigingen, het effect wordt uitgevoerd, updates count dat activeert opnieuw een nieuwe rendering, enzovoort.Om dat patroon te doorbreken, kun je overwegen of de statusupdate wel echt thuishoort in een effect, of dat deze in plaats daarvan door een gebruikersinteractie moet worden geactiveerd, of dat je op een andere waarde kunt vertrouwen.

Soms wil je de meest recente waarde van een bepaalde state of props binnen een effect uitlezen zonder dat die waarde een herhaling van het effect triggert.In die geavanceerde scenario's worden nieuwere API's zoals "effect events" (via useEffectEvent (zie de React-documentatie) of refs kunnen helpen, maar in de meeste praktische gevallen is het veiliger en eenvoudiger om trouw te blijven aan de bestaande afhankelijkheden.

Alles samenvoegen, gebruikmakend van useState en useEffect Het komt uiteindelijk neer op een paar kerngewoonten.Houd de state klein en gefocust, geef de voorkeur aan functionele updates bij het afleiden van nieuwe state uit oude, structureer effecten rondom setup/cleanup-paren, wees eerlijk en expliciet met dependency arrays en respecteer altijd de regels van hooks, zodat React betrouwbaar kan bijhouden wat waar thuishoort. Wanneer je deze principes volgt, blijven je componenten voorspelbaar, gedragen je neveneffecten zich correct en wordt je React-codebasis veel gemakkelijker te ontwikkelen naarmate je app groeit.

Gerelateerd artikel:
Opgelost: hoe je native hooks van Reageer installeert
Gerelateerde berichten: