aslain.dev
0%
01 Hizmetler 02 Hakkımda 03 Projeler 04 Stack 05 Blog 06 İletişim
← Tüm makaleler Webontwikkeling

React Performance Optimaliseren: memo en useMemo

De overgrote meerderheid van de React performance problemen komt niet van slechte algoritmes, maar van onnodige re-renders. Telkens als een component rendert, voert hij zijn subboom opnieuw uit; meestal is dat goedkoop, maar bij grote lijsten, zware berekeningen of diepe bomen begint de UI te haperen. Dit artikel laat zien hoe je renders meet en hoe je ze verstandig vermindert met het drietal memo, useMemo en useCallback.

Waarom rendert React opnieuw?

Een component re-rendert in drie gevallen: wanneer zijn eigen state verandert, wanneer de props die hij ontvangt veranderen, of wanneer zijn ouder rendert. Dat laatste punt wordt het vaakst over het hoofd gezien: wanneer een ouder rendert, re-renderen zijn kinderen standaard, zelfs als hun props nooit veranderd zijn.

Meestal is dat geen probleem; de render van React is gewone JavaScript-uitvoering en hij raakt de echte DOM pas aan als er een werkelijk verschil is. De problemen beginnen wanneer de render zelf duur is: een tabel met duizenden rijen, een complexe grafiek, of een zware lus die bij elke render opnieuw berekend wordt.

Meet voordat je optimaliseert

Voortijdige optimalisatie is de grootste valkuil. Voordat je overal memo strooit, meet waar het echt traag is. Het tabblad Profiler in de React Developer Tools-extensie is hier precies voor gemaakt: start de opname, voer de interactie uit, stop. De flame graph toont welk component hoe vaak en hoeveel milliseconden gerenderd is.

Schakel tijdens de ontwikkeling de optie "Record why each component rendered" van de Profiler in. Als je vaak de renderreden "Parent component rendered" ziet, heb je een kandidaat voor memoization gevonden.

React.memo om een component te onthouden

React.memo omhult een component en slaat de render over als de props niet veranderd zijn volgens een oppervlakkige (shallow) vergelijking. Ideaal voor pure, door props aangestuurde presentatiecomponenten.

import { memo } from 'react';

const UserRow = memo(function UserRow({ name, email }) {
  console.log('render', name);
  return (
    <tr>
      <td>{name}</td>
      <td>{email}</td>
    </tr>
  );
});

Nu re-rendert UserRow niet wanneer de ouder rendert, zolang name en email hetzelfde blijven. Maar pas op: de oppervlakkige vergelijking vergelijkt object- en functie-props op referentie. Maak je bij elke render een nieuw object of een nieuwe functie aan, dan doet memo niets, omdat de referentie elke keer verandert.

useMemo om dure berekeningen te cachen

useMemo onthoudt een waarde: het voert de berekening niet opnieuw uit tenzij de afhankelijkheden die je opgeeft veranderen. Het heeft twee hoofdgebruiken: het overslaan van echt dure berekeningen, en het stabiel houden van een object-/array-referentie die je aan met memo omhulde kinderen geeft.

const sortedUsers = useMemo(() => {
  return [...users].sort((a, b) => a.name.localeCompare(b.name));
}, [users]);

Hier draait het sorteren alleen wanneer de array users verandert; wanneer een andere state in de ouder een render veroorzaakt, wordt het sorteren overgeslagen. Belangrijke regel: wees eerlijk met de afhankelijkhedenarray. Elke externe variabele die je in de berekening gebruikt, moet erin staan, anders werk je met verouderde (stale) waarden.

Geef niet toe aan de verleiding om alles in useMemo te wikkelen. De vergelijking en de cache zelf kosten ook iets; een goedkope berekening als a + b memoizen is netto verlies.

useCallback om een functiereferentie te stabiliseren

useCallback is eigenlijk de functie-specifieke vorm van useMemo: het behoudt de referentie van een functie tot de afhankelijkheden veranderen. De echte waarde komt naar voren wanneer je een callback doorgeeft aan een met memo omhuld kind.

const handleDelete = useCallback((id) => {
  setUsers((prev) => prev.filter((u) => u.id !== id));
}, []);

// het memo-kind re-rendert niet meer door deze prop
<UserRow user={user} onDelete={handleDelete} />

Met een lege afhankelijkhedenarray wordt de functie eenmaal bij de eerste render aangemaakt en houdt altijd dezelfde referentie. Merk op dat ik de functionele (updater) vorm van setUsers gebruik — zo vermijd ik users aan de afhankelijkheden toe te voegen en houd ik de callback stabiel.

Veelgemaakte fouten

  • Een nieuw objectliteraal aan een memo-component geven: style={{ margin: 8 }} is bij elke render een nieuwe referentie en doorbreekt de memoization. Verplaats het constante object buiten het component of bouw het met useMemo.
  • De index als lijstsleutel gebruiken: in lijsten met sorteren of toevoegen/verwijderen veroorzaakt key={index} verkeerde matches en onnodige renders; gebruik een stabiele id.
  • Overal optimalisatie strooien: memo/useMemo zonder meten toegevoegd schaadt de leesbaarheid en levert geen voordeel op.

Een opmerking over React 19 en de compiler

De React Compiler die rond React 19 geïntroduceerd is, wil het grootste deel van deze memoization automatisch tijdens het bouwen doen; met andere woorden, de toekomstige behoefte om useMemo/useCallback met de hand te schrijven kan afnemen. Toch is de compiler nog niet in elk project ingeschakeld, en deze concepten begrijpen blijft essentieel voor bestaande codebases. Het principe "eerst meten, dan denken, als laatste optimaliseren" geldt in elke versie.

Veelgestelde vragen

Wat is het verschil tussen memo, useMemo en useCallback?

memo omhult een component en slaat de render over zolang de props niet veranderen. useMemo cacht een waarde. useCallback houdt een functiereferentie stabiel; het is eigenlijk een afkorting voor useMemo(() => fn, deps).

Moet ik elk component in memo wikkelen?

Nee. De vergelijking kost ook iets en is netto verlies voor goedkope renders. Memoize alleen componenten waarvan je met de Profiler hebt aangetoond dat ze echt duur zijn of heel vaak renderen.

Maakt useMemo het echt sneller?

Alleen als de berekening duur genoeg is. Bij simpele rekenkunde of kleine arrays kan de kosten van de cache groter zijn dan het voordeel. Meet en beslis.

Wil je je React-applicatie versnellen? Om je renders te profileren en knelpunten met echte metingen op te lossen, neem contact met me op.

Bu kategorideki tüm yazılar →

Devamı için