React Hooks — zijn functies waarmee je state, effecten, context en andere React-mogelijkheden kunt gebruiken in functionele componenten, zonder klassen te schrijven. Geïntroduceerd in React 16.8, hebben hooks de ontwikkelingsaanpak radicaal veranderd door complexe patronen zoals HOC en render props te vervangen. Volgens React, 2024 zijn er 15 ingebouwde hooks die alle scenario’s voor componentbeheer dekken.
Belangrijkste
React Hooks — een set ingebouwde functies in React waarmee je ‘verbinding kunt maken’ met de interne mechanismen van het framework zonder klassen te maken. Vóór React 16.8 konden functionele componenten alleen props ontvangen en JSX retourneren — elke complexe logica vereiste een klassecomponent.
Het probleem van klassecomponenten — scheiding van gerelateerde logica. Code die bij één entiteit hoorde (bijvoorbeeld een WebSocket-abonnement) werd gedwongen verdeeld tussen componentDidMount, componentDidUpdate en componentWillUnmount. Hooks lossen dit op: alle logica wordt gegroepeerd in één useEffect.
Het tweede probleem — hergebruik van logica. Voor hergebruik van statelogica tussen klassecomponenten moesten HOC (componenten van hogere orde) of render props worden toegepast — beide patronen creëerden extra nesting in de componentenboom. Aangepaste hooks lossen dit probleem op zonder nesting.
useState — de meest basale hook. Het accepteert een beginwaarde en retourneert een tuple: de huidige waarde en een functie om deze bij te werken. In tegenstelling tot this.setState in klassen, voegt useState objecten niet samen — de nieuwe state vervangt volledig de vorige voor een specifieke aanroep van useState.
import React, { useState } from 'react';
function Form() {
const [text, setText] = useState('');
const [submitting, setSubmitting] = useState(false);
async function handleSubmit() {
setSubmitting(true);
await fetch('/api/submit', {
method: 'POST',
body: JSON.stringify({ text })
});
setSubmitting(false);
}
return (
<div>
<input
value={text}
onChange={e => setText(e.target.value)}
/>
<button onClick={handleSubmit}
disabled={submitting}>
{submitting ? 'Saving...' : 'Submit'}
</button>
</div>
);
}
De setter van useState kan een functie accepteren die de vorige waarde ontvangt en een nieuwe retourneert. Functionele vorm is verplicht wanneer de nieuwe state wordt berekend uit de vorige en gebatchte aanroepen van setState mogelijk zijn — elke aanroep krijgt de actuele vorige waarde.
useEffect — hook voor het uitvoeren van neveneffecten in functionele componenten. Het accepteert twee argumenten: een effectfunctie en een afhankelijkhedenarray. React voert het effect uit na elke render, maar bij het opgeven van afhankelijkheden — alleen wanneer een van hen is veranderd.
Een lege afhankelijkhedenarray [] betekent dat het effect eenmaal wordt uitgevoerd na de eerste render (analoog aan componentDidMount). Het retourneren van een functie uit het effect is de cleanup, die wordt uitgevoerd bij het ontmantelen (analoog aan componentWillUnmount) en voordat het effect opnieuw wordt uitgevoerd.
import React, { useState, useEffect } from 'react';
function ChatRoom({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
const socket = new WebSocket(`wss://chat/${roomId}`);
socket.onmessage = (event) => {
setMessages(prev => [...prev, JSON.parse(event.data)]);
};
// Cleanup - socket sluiten
return () => socket.close();
}, [roomId]);
return <ul>
{messages.map((msg, i) =>
<li key={i}>{msg.text}</li>
)}
</ul>;
}
De afhankelijkheden van useEffect — zijn de enige verbinding tussen het effect en de buitenwereld. Als in het effect een variabele uit props of state wordt gebruikt, moet deze in de afhankelijkhedenarray staan. Het negeren van deze regel is de bron van bugs met verouderde gegevens (stale closure).
useContext — hook voor het lezen van de contextwaarde. Het accepteert het contextobject dat is gemaakt met React.createContext en retourneert de huidige waarde. Als de context verandert, wordt de component automatisch opnieuw gerenderd met de nieuwe waarde.
Vóór de komst van useContext vereiste toegang tot context in functionele componenten een omhulling in Context.Consumer, wat nesting creëerde. useContext elimineert Consumer volledig — het is voldoende om de hook aan te roepen op elke plek in de functionele component.
import React, { createContext, useContext } from 'react';
const ThemeContext = createContext('licht');
function ThemedButton() {
const theme = useContext(ThemeContext);
return <button
className={`btn btn-${theme}`}
>
{theme === 'dark' ? 'moon' : '☀️'}
</button>;
}
useContext is geen vervanging voor Redux of globale state. React raadt aan context te gebruiken voor zelden veranderende waarden — thema, taal, autorisatie. Voor veelvuldig bijgewerkte gegevens zal elke re-render alle consumenten van de context opnieuw berekenen, wat de prestaties kan verminderen.
useReducer — alternatief voor useState voor states met complexe updatelogica. Het accepteert een reducer-functie en een begintoestand, retourneert de huidige state en een dispatch-functie. De reducer ontvangt de huidige state en actie, retourneert een nieuwe state — een pure functie zonder neveneffecten.
useReducer is vooral nuttig wanneer meerdere acties (set, reset, increment, addItem) één state beheren. In plaats van meerdere useState met verschillende setters — één reducer met duidelijke actietypen. Dit maakt de logica voorspelbaar en gemakkelijk te testen.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { ...state, count: state.count + 1 };
case 'setStep':
return { ...state, step: action.step };
case 'reset':
return { count: 0, step: 1 };
default:
throw new Error('Unknown action: ' + action.type);
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, {
count: 0,
step: 1
});
return (
<>
<p>Count: {state.count}</p>
<button
onClick={() => dispatch({ type: 'increment' })}>+</button>
<button
onClick={() => dispatch({ type: 'reset' })}>Reset</button>
</>
);
}
useRef creëert een veranderlijk object dat behouden blijft tussen renders. Het wijzigen van current veroorzaakt geen re-render. Hoofdtoepassing — toegang tot DOM-elementen en opslag van muteerbare waarden (timers, vorige waarden, bibliotheekinstanties).
useMemo memoiseert het resultaat van een functie. Het herberekent de waarde alleen wanneer een van de afhankelijkheden verandert. Gebruik voor dure berekeningen — filteren van grote arrays, sorteren, gegevens formatteren. useCallback — een speciaal geval van useMemo voor het memoizeren van functies.
import React, { useRef, useMemo, useCallback } from 'react';
function SearchPage({ items, query, onItemSelect }) {
const inputRef = useRef(null);
// Autofocus bij montage
useEffect(() => {
inputRef.current?.focus();
}, []);
// Gefilterd resultaat memoizeren
const filtered = useMemo(() => {
return items.filter(item =>
item.name.toLowerCase()
.includes(query.toLowerCase())
);
}, [items, query]);
// Callback memoizeren
const handleSelect = useCallback((id) => {
onItemSelect(id);
}, [onItemSelect]);
return (
<div>
<input ref={inputRef}
value={query}
onChange={e => setQuery(e.target.value)}
/>
<ul>
{filtered.map(item =>
<li key={item.id}>
<button
onClick={() => handleSelect(item.id)}>
{item.name}
</button>
</li>
)}
</ul>
</div>
);
}
Belangrijke regel: useRef, useMemo en useCallback — optimalisatiegereedschappen, geen verplichte constructies. Wikkel niet alles in useMemo — meet eerst de prestaties. Voortijdige optimalisatie maakt code complexer. React rendert componenten snel; useMemo is alleen nodig wanneer berekeningen echt duur zijn (O(n²) en hoger).
React heeft twee strikte regels voor hooks geïntroduceerd. Eerste: roep hooks alleen aan op het hoogste niveau — niet binnen voorwaarden, lussen of geneste functies. Tweede: roep hooks alleen aan vanuit React functionele componenten of aangepaste hooks. De ESLint-plugin eslint-plugin-react-hooks controleert automatisch beide regels.
De meest voorkomende fout — schending van de aanroepvolgorde van hooks. React vertrouwt op de volgorde van hook-aanroepen tussen renders. Als een hook zich binnen een voorwaarde bevindt en niet is aangeroepen in een van de renders, verschuiven alle volgende hooks, wat bugs veroorzaakt.
| Fout | Wat gebeurt er | Oplossing |
|---|---|---|
| Hook in voorwaarde | Volgorde van hooks raakt verstoord | Verplaats de voorwaardelogica naar binnen de hook |
| Stale closure | Effect gebruikt oude waarde | Voeg alle afhankelijkheden toe aan de array |
| Oneindige lus | useEffect zonder afhankelijkheden wijzigt state | Specificeer afhankelijkheden of gebruik useReducer |
| Ref wijzigen in render | Neveneffect in functielichaam | Verplaats mutaties naar useEffect |
De tweede veelgemaakte fout — stale closure in useEffect. Als in het effect een variabele wordt gebruikt die niet in de afhankelijkheden is gespecificeerd, ‘vangt’ het effect de waarde op het moment van het maken van de sluiting. Oplossing — neem altijd alle gebruikte variabelen op in de afhankelijkhedenarray. Als er te veel afhankelijkheden zijn, splits het effect dan in meerdere delen.
Veelgestelde vragen
React vertrouwt op de volgorde van hook-aanroepen tussen renders. Als een hook is aangeroepen in een voorwaarde en niet heeft gewerkt in een van de renders, ‘verschuiven’ alle volgende hooks — hun state raakt vermengd. De ESLint-regel hooks/exhaustive-deps waarschuwt hiervoor.
useLayoutEffect is synchroon en wordt uitgevoerd voordat de browser wijzigingen tekent. useEffect is asynchroon — na het tekenen. Gebruik useLayoutEffect alleen wanneer je DOM moet meten of wijzigen voordat de gebruiker wijzigingen ziet (popover-positie, scroll-herstel).
Gebruik AbortController binnen useEffect. Maak een controller, geef signal door aan fetch en roep in de cleanup-functie controller.abort() aan. Voor oude browsers volstaat een cancelled-vlag: return () => { cancelled = true; } met controle vóór setState.
Een aangepaste hook — een JavaScript-functie met het voorvoegsel use die ingebouwde hooks gebruikt. Maak deze aan wanneer dezelfde logica met hooks wordt herhaald in twee of meer componenten. Voorbeelden: useDebounce, useLocalStorage, useMediaQuery. Aangepaste hooks vervangen HOC en render props.
In Strict Mode monteert en ontmantelt React de component opzettelijk twee keer in de ontwikkelmodus om fouten met niet-opgeruimde effecten te vinden. Als de cleanup correct is geschreven, veroorzaakt de dubbele aanroep geen problemen. In de productiemodus gebeurt dit niet.
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