Een uitgebreide gids voor het optimaliseren van de prestaties van React-applicaties met useMemo, useCallback en React.memo. Leer onnodige re-renders te voorkomen en de gebruikerservaring te verbeteren.
React Performance Optimalisatie: useMemo, useCallback en React.memo beheersen
React, een populaire JavaScript-bibliotheek voor het bouwen van gebruikersinterfaces, staat bekend om zijn component-gebaseerde architectuur en declaratieve stijl. Naarmate applicaties echter complexer worden, kan de prestatie een probleem worden. Onnodige re-renders van componenten kunnen leiden tot trage prestaties en een slechte gebruikerservaring. Gelukkig biedt React verschillende tools om de prestaties te optimaliseren, waaronder useMemo
, useCallback
en React.memo
. Deze gids duikt in deze technieken en biedt praktische voorbeelden en bruikbare inzichten om u te helpen bij het bouwen van krachtige React-applicaties.
React Re-renders begrijpen
Voordat u in de optimalisatietechnieken duikt, is het cruciaal om te begrijpen waarom re-renders in React plaatsvinden. Wanneer de state of props van een component veranderen, activeert React een re-render van die component en mogelijk ook van de kindcomponenten. React gebruikt een virtuele DOM om de daadwerkelijke DOM efficiënt bij te werken, maar overmatige re-renders kunnen nog steeds van invloed zijn op de prestaties, vooral in complexe applicaties. Stel u een wereldwijd e-commerceplatform voor waar de productprijzen frequent worden bijgewerkt. Zonder optimalisatie kan zelfs een kleine prijswijziging re-renders in de gehele productlijst activeren, wat van invloed is op het browsen van de gebruiker.
Waarom componenten opnieuw renderen
- State wijzigingen: Wanneer de state van een component wordt bijgewerkt met behulp van
useState
ofuseReducer
, rendert React de component opnieuw. - Prop wijzigingen: Als een component nieuwe props van de bovenliggende component ontvangt, zal deze opnieuw renderen.
- Bovenliggende re-renders: Wanneer een bovenliggende component opnieuw rendert, renderen de kindcomponenten standaard ook opnieuw, ongeacht of hun props zijn gewijzigd.
- Context wijzigingen: Componenten die een React Context consumeren, zullen opnieuw renderen wanneer de contextwaarde verandert.
Het doel van prestatieoptimalisatie is om onnodige re-renders te voorkomen, zodat componenten alleen worden bijgewerkt wanneer hun gegevens daadwerkelijk zijn gewijzigd. Denk aan een scenario met real-time datavisualisatie voor beursanalyse. Als de grafiekcomponenten onnodig opnieuw renderen bij elke kleine data-update, reageert de applicatie niet meer. Het optimaliseren van re-renders zorgt voor een soepele en responsieve gebruikerservaring.
Introductie van useMemo: Dure berekeningen memoiseren
useMemo
is een React hook die het resultaat van een berekening memoiseert. Memoization is een optimalisatietechniek die de resultaten van dure functieaanroepen opslaat en die resultaten hergebruikt wanneer dezelfde inputs opnieuw voorkomen. Dit voorkomt dat de functie onnodig opnieuw hoeft te worden uitgevoerd.
Wanneer useMemo te gebruiken
- Dure berekeningen: Wanneer een component een computationeel intensieve berekening moet uitvoeren op basis van zijn props of state.
- Referentiële gelijkheid: Wanneer een waarde als prop wordt doorgegeven aan een kindcomponent dat afhankelijk is van referentiële gelijkheid om te bepalen of er opnieuw moet worden gerenderd.
Hoe useMemo werkt
useMemo
heeft twee argumenten:
- Een functie die de berekening uitvoert.
- Een array met afhankelijkheden.
De functie wordt alleen uitgevoerd wanneer een van de afhankelijkheden in de array verandert. Anders retourneert useMemo
de eerder gememoiseerde waarde.
Voorbeeld: De Fibonacci-reeks berekenen
De Fibonacci-reeks is een klassiek voorbeeld van een computationeel intensieve berekening. Laten we een component maken die het n-de Fibonacci-getal berekent met behulp van useMemo
.
import React, { useState, useMemo } from 'react';
function Fibonacci({ n }) {
const fibonacciNumber = useMemo(() => {
console.log('Calculating Fibonacci...'); // Toont wanneer de berekening wordt uitgevoerd
function calculateFibonacci(num) {
if (num <= 1) {
return num;
}
return calculateFibonacci(num - 1) + calculateFibonacci(num - 2);
}
return calculateFibonacci(n);
}, [n]);
return Fibonacci({n}) = {fibonacciNumber}
;
}
function App() {
const [number, setNumber] = useState(5);
return (
setNumber(parseInt(e.target.value))}
/>
);
}
export default App;
In dit voorbeeld wordt de functie calculateFibonacci
alleen uitgevoerd wanneer de prop n
verandert. Zonder useMemo
zou de functie bij elke re-render van de component Fibonacci
worden uitgevoerd, zelfs als n
hetzelfde bleef. Stel je voor dat deze berekening plaatsvindt op een wereldwijd financieel dashboard - elke tik van de markt veroorzaakt een volledige herberekening, wat leidt tot aanzienlijke vertraging. useMemo
voorkomt dat.
Introductie van useCallback: Functies memoiseren
useCallback
is een andere React hook die functies memoiseert. Het voorkomt het aanmaken van een nieuw functie-exemplaar bij elke render, wat met name handig kan zijn bij het doorgeven van callbacks als props aan kindcomponenten.
Wanneer useCallback te gebruiken
- Callbacks doorgeven als props: Bij het doorgeven van een functie als prop aan een kindcomponent dat
React.memo
ofshouldComponentUpdate
gebruikt om re-renders te optimaliseren. - Event handlers: Bij het definiëren van event handler-functies binnen een component om onnodige re-renders van kindcomponenten te voorkomen.
Hoe useCallback werkt
useCallback
heeft twee argumenten:
- De functie die moet worden gememoiseerd.
- Een array met afhankelijkheden.
De functie wordt alleen opnieuw gemaakt wanneer een van de afhankelijkheden in de array verandert. Anders retourneert useCallback
hetzelfde functie-exemplaar.
Voorbeeld: Klik op een knop verwerken
Laten we een component maken met een knop die een callback-functie activeert. We gebruiken useCallback
om de callback-functie te memoiseren.
import React, { useState, useCallback } from 'react';
function Button({ onClick, children }) {
console.log('Button re-rendered'); // Toont wanneer de Button opnieuw rendert
return ;
}
const MemoizedButton = React.memo(Button);
function App() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
console.log('Button clicked');
setCount((prevCount) => prevCount + 1);
}, []); // Lege afhankelijkheidsarray betekent dat de functie slechts één keer wordt gemaakt
return (
Count: {count}
Increment
);
}
export default App;
In dit voorbeeld wordt de functie handleClick
slechts één keer aangemaakt omdat de afhankelijkheidsarray leeg is. Wanneer de component App
opnieuw rendert vanwege de state-wijziging count
, blijft de functie handleClick
hetzelfde. De component MemoizedButton
, verpakt met React.memo
, zal alleen opnieuw renderen als de props veranderen. Omdat de prop onClick
(handleClick
) hetzelfde blijft, rendert de component Button
niet onnodig opnieuw. Stel je een interactieve kaarttoepassing voor. Elke keer dat een gebruiker interactie heeft, kunnen tientallen knopcomponenten worden beïnvloed. Zonder useCallback
zouden deze knoppen onnodig opnieuw renderen, wat een traag gevoel geeft. Het gebruik van useCallback
zorgt voor een soepelere interactie.
Introductie van React.memo: Componenten memoiseren
React.memo
is een higher-order component (HOC) die een functionele component memoiseert. Het voorkomt dat de component opnieuw wordt weergegeven als de props niet zijn gewijzigd. Dit is vergelijkbaar met PureComponent
voor class componenten.
Wanneer React.memo te gebruiken
- Pure Components: Wanneer de output van een component uitsluitend afhankelijk is van zijn props en het geen eigen state heeft.
- Dure rendering: Wanneer het renderingproces van een component computationeel duur is.
- Frequente re-renders: Wanneer een component vaak opnieuw wordt weergegeven, ook al zijn de props niet gewijzigd.
Hoe React.memo werkt
React.memo
omhult een functionele component en vergelijkt de vorige en volgende props ondiep. Als de props hetzelfde zijn, zal de component niet opnieuw renderen.
Voorbeeld: Een gebruikersprofiel weergeven
Laten we een component maken dat een gebruikersprofiel weergeeft. We gebruiken React.memo
om onnodige re-renders te voorkomen als de gebruikersgegevens niet zijn gewijzigd.
import React from 'react';
function UserProfile({ user }) {
console.log('UserProfile re-rendered'); // Toont wanneer de component opnieuw rendert
return (
Name: {user.name}
Email: {user.email}
);
}
const MemoizedUserProfile = React.memo(UserProfile, (prevProps, nextProps) => {
// Aangepaste vergelijkingsfunctie (optioneel)
return prevProps.user.id === nextProps.user.id; // Alleen opnieuw renderen als de gebruikers-ID verandert
});
function App() {
const [user, setUser] = React.useState({
id: 1,
name: 'John Doe',
email: 'john.doe@example.com',
});
const updateUser = () => {
setUser({ ...user, name: 'Jane Doe' }); // De naam wijzigen
};
return (
);
}
export default App;
In dit voorbeeld zal de component MemoizedUserProfile
alleen opnieuw renderen als de prop user.id
verandert. Zelfs als andere eigenschappen van het object user
veranderen (bijvoorbeeld de naam of e-mail), zal de component niet opnieuw renderen, tenzij de ID anders is. Deze aangepaste vergelijkingsfunctie binnen `React.memo` maakt fijne controle mogelijk over wanneer de component opnieuw rendert. Denk aan een social media platform met constant bijgewerkte gebruikersprofielen. Zonder `React.memo` zou het wijzigen van de status of profielfoto van een gebruiker een volledige re-render van de profielcomponent veroorzaken, zelfs als de kerngebruikersgegevens hetzelfde blijven. `React.memo` maakt gerichte updates mogelijk en verbetert de prestaties aanzienlijk.
useMemo, useCallback en React.memo combineren
Deze drie technieken zijn het meest effectief wanneer ze samen worden gebruikt. useMemo
memoiseert dure berekeningen, useCallback
memoiseert functies en React.memo
memoiseert componenten. Door deze technieken te combineren, kunt u het aantal onnodige re-renders in uw React-applicatie aanzienlijk verminderen.
Voorbeeld: Een complexe component
Laten we een complexere component maken die demonstreert hoe deze technieken te combineren.
import React, { useState, useCallback, useMemo } from 'react';
function ListItem({ item, onUpdate, onDelete }) {
console.log(`ListItem ${item.id} re-rendered`); // Toont wanneer de component opnieuw rendert
return (
{item.text}
);
}
const MemoizedListItem = React.memo(ListItem);
function List({ items, onUpdate, onDelete }) {
console.log('List re-rendered'); // Toont wanneer de component opnieuw rendert
return (
{items.map((item) => (
))}
);
}
const MemoizedList = React.memo(List);
function App() {
const [items, setItems] = useState([
{ id: 1, text: 'Item 1' },
{ id: 2, text: 'Item 2' },
{ id: 3, text: 'Item 3' },
]);
const handleUpdate = useCallback((id) => {
setItems((prevItems) =>
prevItems.map((item) =>
item.id === id ? { ...item, text: `Updated ${item.text}` } : item
)
);
}, []);
const handleDelete = useCallback((id) => {
setItems((prevItems) => prevItems.filter((item) => item.id !== id));
}, []);
const memoizedItems = useMemo(() => items, [items]);
return (
);
}
export default App;
In dit voorbeeld:
useCallback
wordt gebruikt om de functieshandleUpdate
enhandleDelete
te memoiseren, waardoor wordt voorkomen dat ze bij elke render opnieuw worden gemaakt.useMemo
wordt gebruikt om de arrayitems
te memoiseren, waardoor de componentList
niet opnieuw wordt weergegeven als de array-referentie niet is gewijzigd.React.memo
wordt gebruikt om de componentenListItem
enList
te memoiseren, waardoor wordt voorkomen dat ze opnieuw worden weergegeven als hun props niet zijn gewijzigd.
Deze combinatie van technieken zorgt ervoor dat de componenten alleen opnieuw renderen wanneer dat nodig is, wat leidt tot aanzienlijke prestatieverbeteringen. Stel je een grootschalige projectmanagementtool voor waarbij lijsten met taken constant worden bijgewerkt, verwijderd en opnieuw worden geordend. Zonder deze optimalisaties zou elke kleine wijziging in de takenlijst een reeks re-renders activeren, waardoor de applicatie traag en niet-responsief wordt. Door strategisch gebruik te maken van useMemo
, useCallback
en React.memo
, kan de applicatie prestatiegericht blijven, zelfs met complexe gegevens en frequente updates.
Aanvullende optimalisatietechnieken
Hoewel useMemo
, useCallback
en React.memo
krachtige tools zijn, zijn ze niet de enige opties voor het optimaliseren van de React-prestaties. Hier zijn een paar extra technieken om te overwegen:
- Code splitting: Splits uw applicatie op in kleinere stukken die op aanvraag kunnen worden geladen. Dit vermindert de initiële laadtijd en verbetert de algehele prestaties.
- Lazy loading: Laad componenten en resources alleen wanneer ze nodig zijn. Dit kan met name handig zijn voor afbeeldingen en andere grote assets.
- Virtualisatie: Render alleen het zichtbare gedeelte van een grote lijst of tabel. Dit kan de prestaties aanzienlijk verbeteren bij het verwerken van grote datasets. Bibliotheken zoals
react-window
enreact-virtualized
kunnen hierbij helpen. - Debouncing en throttling: Beperk de snelheid waarmee functies worden uitgevoerd. Dit kan handig zijn voor het afhandelen van gebeurtenissen zoals scrollen en resizing.
- Onveranderlijkheid: Gebruik onveranderlijke datastructuren om onbedoelde mutaties te voorkomen en de detectie van wijzigingen te vereenvoudigen.
Algemene overwegingen voor optimalisatie
Bij het optimaliseren van React-applicaties voor een wereldwijd publiek is het belangrijk om rekening te houden met factoren zoals netwerklatentie, apparaatmogelijkheden en lokalisatie. Hier zijn een paar tips:
- Content Delivery Networks (CDN's): Gebruik een CDN om statische assets te serveren vanaf locaties die dichter bij uw gebruikers liggen. Dit vermindert de netwerklatentie en verbetert de laadtijden.
- Afbeelding optimalisatie: Optimaliseer afbeeldingen voor verschillende schermformaten en resoluties. Gebruik compressietechnieken om de bestandsgroottes te verkleinen.
- Lokalisatie: Laad alleen de benodigde taalbronnen voor elke gebruiker. Dit vermindert de initiële laadtijd en verbetert de gebruikerservaring.
- Adaptief laden: Detecteer de netwerkverbinding en apparaatmogelijkheden van de gebruiker en pas het gedrag van de applicatie dienovereenkomstig aan. U kunt bijvoorbeeld animaties uitschakelen of de beeldkwaliteit verminderen voor gebruikers met trage netwerkverbindingen of oudere apparaten.
Conclusie
Het optimaliseren van de prestaties van React-applicaties is cruciaal voor het leveren van een soepele en responsieve gebruikerservaring. Door technieken als useMemo
, useCallback
en React.memo
te beheersen en door wereldwijde optimalisatiestrategieën te overwegen, kunt u krachtige React-applicaties bouwen die kunnen worden opgeschaald om te voldoen aan de behoeften van een divers gebruikersbestand. Vergeet niet om uw applicatie te profileren om knelpunten in de prestaties te identificeren en deze optimalisatietechnieken strategisch toe te passen. Optimaliseer niet vroegtijdig - concentreer u op gebieden waar u de meeste impact kunt bereiken.
Deze gids biedt een solide basis voor het begrijpen en implementeren van React-prestatieoptimalisaties. Als u React-applicaties blijft ontwikkelen, vergeet dan niet om prioriteit te geven aan de prestaties en continu op zoek te gaan naar nieuwe manieren om de gebruikerservaring te verbeteren.