React 18 heeft useSyncExternalStore toegevoegd, een hook waarmee componenten gegevens kunnen lezen uit elke bron die buiten de React-tree leeft — browser-API's, WebSockets of third-party stores — zonder de visuele glitches die het oude patroon van het spiegelen van die gegevens met useState en useEffect teisterden. Als je ooit een UI-flikkering hebt gezien omdat een deel een oude venstergrootte toonde terwijl een ander deel al de nieuwe reflecteerde, dan is deze hook de oplossing waar je op hebt gewacht.
Waarom externe state een nieuwe hook nodig heeft
React-componenten hebben altijd al kunnen abonneren op zaken die buiten hun eigen scope bestaan: het window-object, een Redux-store, een WebSocket-verbinding. De conventionele aanpak was om een abonnement op te zetten binnen een useEffect, binnenkomende waarden naar de lokale state te pushen via setState en React te laten re-renderen. Dat werkt prima wanneer React synchroon rendert, maar React 18 heeft concurrent rendering geïntroduceerd, waarbij een component meerdere keren gerenderd kan worden voordat de browser daadwerkelijk tekent (paints). In die modus kan het "mirror-into-local-state"-patroon tearing veroorzaken — verschillende delen van de UI die tijdens een enkele render-cyclus verschillende snapshots van dezelfde externe waarde lezen.
useSyncExternalStore is gebouwd om tearing te voorkomen. Het vraagt de externe bron om een snapshot (de huidige waarde) en een subscribe-functie (hoe je op de hoogte wordt gesteld van wijzigingen). React roept subscribe aan wanneer de component mount en meldt zich automatisch af (unsubscribes) bij unmount. Telkens wanneer de snapshot die door getSnapshot wordt geretourneerd verandert, plant React een render in die de dezelfde snapshot leest voor de gehele component-tree, wat consistentie garandeert.
De twee vereiste functies
| Functie | Wat het doet |
|---|---|
| subscribe | Ontvangt een callback die moet worden aangeroepen wanneer de externe waarde verandert. Het retourneert een unsubscribe-functie die React aanroept bij het opschonen (cleanup). |
| getSnapshot | Retourneert de huidige waarde van de externe bron. Het moet referentieel stabiel zijn — hetzelfde functie-object bij elke render — zodat React snapshots correct kan vergelijken. |
Een minimale implementatie voor de online-status van de browser ziet er als volgt uit:
function useOnlineStatus() {
return useSyncExternalStore(
(onChange) => {
window.addEventListener('online', onChange);
window.addEventListener('offline', onChange);
return () => {
window.removeEventListener('online', onChange);
window.removeEventListener('offline', onChange);
};
},
() => navigator.onLine
);
}
Wanneer een component useOnlineStatus() aanroept, zal React deze alleen opnieuw renderen wanneer navigator.onLine daadwerkelijk verandert, en elke render zal dezelfde waarde zien.
Regels om te voorkomen dat de hook ontspoort
Retourneer het kleinste stukje gegevens dat een component nodig heeft.
Als de externe store een groot object bevat, maar een component alleen geïnteresseerd is in één veld, retourneer dan alleen dat veld. Grotere returns veroorzaken onnodige renders.Memoise
getSnapshot.
Als je de functie bij elke render opnieuw aanmaakt, behandelt React dit als een nieuwe bron en kan er een oneindige lus ontstaan. Wikkel het inuseCallbackof definieer het buiten de component.Vermijd het retourneren van nieuwe objecten bij elke aanroep.
Het retourneren van een nieuw object (bijv.{ count: store.getCount() }) creëert bij elke render een nieuwe referentie, waardoor React denkt dat de snapshot is gewijzigd en een oneindige render-loop wordt getriggerd. Gebruik primitieve types, strings, getallen of gememoïseerde objecten.Gebruik het niet voor interne component-state.
useStateis nog steeds het juiste hulpmiddel voor gegevens die alleen binnen de component leven.useSyncExternalStorevoegt overhead toe die niet nodig is voor lokale state.
Wanneer je het moet gebruiken
- Browser-API's – venstergrootte, media queries,
navigator.onLine, batterijstatus. - Framework-agnostische stores – Zustand, Redux, MobX, of elke custom store die een subscription-API biedt.
- Mutable bronnen die buiten React worden bijgewerkt – WebSocket-berichten, IndexedDB-wijzigingsgebeurtenissen, Service Worker-meldingen.
Als je gegevensbron al binnen React leeft (bijv. de state van een bovenliggende component), houd dan vast aan useState of context.
Wat de community zegt
Early adopters melden dat useSyncExternalStore de flikkering elimineert die ze zagen bij het wijzigen van de venstergrootte in een concurrent-rendering-omgeving. Sommige bibliotheken zijn hun interne hooks al overgestapt naar deze API, wat meer voorspelbaar gedrag over verschillende React-versies belooft. Het nadeel is een iets hogere mentale belasting: ontwikkelaars moeten nadenken over de stabiliteit van de twee functies en moeten voorkomen dat ze bij elke aanroep nieuwe objecten retourneren.
Wat je hierna moet volgen
De komende releases van React kunnen de contracten rondom useSyncExternalStore aanscherpen, mogelijk door het toevoegen van ingebouwde helpers voor veelvoorkomende browser-API's. Houd de officiële React-blog in de gaten voor eventuele deprecaties van het oude useEffect-spiegelpatroon. In de tussentijd is de hook stabiel en maakt deze deel uit van de publieke API, zodat je bestaande code voor externe state veilig kunt refactoren.
Snelle proefrit
Een live demo die window-size synchronisatie, online/offline detectie en een eenvoudige Zustand-store laat zien, is te vinden op https://usesyncexternalstore.vercel.app/. De broncode, inclusief commentaar, is beschikbaar op https://github.com/dev48v/usesyncexternalstore.
Kernpunt: useSyncExternalStore biedt React een betrouwbare brug naar alle veranderlijke data die buiten de render-cyclus leeft, waardoor tearing en onnodige renders worden voorkomen. Pas het toe op externe bronnen, houd de snapshot minimaal en stabiel, en laat React het zware werk voor de consistentie doen.
