Mestr Reacts useCallback hook for at optimere funktioners ydeevne, forhindre unødvendige re-renders og bygge effektive og performante applikationer.
React useCallback: Funktionsmemoisering og Afhængighedsoptimering
React er et kraftfuldt JavaScript-bibliotek til at bygge brugergrænseflader, og det bruges i vid udstrækning af udviklere over hele verden. Et af de vigtigste aspekter ved at bygge effektive React-applikationer er at håndtere komponenters re-renders. Unødvendige re-renders kan have en betydelig indvirkning på ydeevnen, især i komplekse applikationer. React tilbyder værktøjer som useCallback for at hjælpe udviklere med at optimere funktioners ydeevne og kontrollere, hvornår funktioner genoprettes, og dermed forbedre den overordnede applikationseffektivitet. Dette blogindlæg dykker ned i useCallback-hook'en og forklarer dens formål, fordele, og hvordan man effektivt bruger den til at optimere sine React-komponenter.
Hvad er useCallback?
useCallback er en React Hook, der memoiserer en funktion. Memoisering er en teknik til ydeevneoptimering, hvor resultaterne af dyre funktionskald caches, og efterfølgende kald til funktionen returnerer det cachede resultat, hvis inputtet ikke har ændret sig. I Reacts kontekst hjælper useCallback med at forhindre unødvendige genoprettelser af funktioner inden i funktionelle komponenter. Dette er især nyttigt, når man sender funktioner som props til børnekomponenter.
Her er den grundlæggende syntaks:
const memoizedCallback = useCallback(
() => {
// Function logic
},
[dependency1, dependency2, ...]
);
Lad os gennemgå de vigtigste dele:
memoizedCallback: Dette er variablen, der vil indeholde den memoisede funktion.useCallback: React Hook'en.() => { ... }: Dette er den funktion, du vil moise. Den indeholder den logik, du vil udføre.[dependency1, dependency2, ...]: Dette er en liste af afhængigheder. Den memoisede funktion vil kun blive genoprettet, hvis en af afhængighederne ændres. Hvis afhængighedslisten er tom ([]), vil funktionen kun blive oprettet én gang under den indledende rendering og vil forblive den samme for alle efterfølgende renderinger.
Hvorfor bruge useCallback? Fordelene
Brug af useCallback giver flere fordele for optimering af React-applikationer:
- Forebyggelse af unødvendige re-renders: Den primære fordel er at forhindre børnekomponenter i at re-rendere unødvendigt. Når en funktion sendes som en prop til en børnekomponent, vil React behandle den som en ny prop ved hver rendering, medmindre du memoiserer funktionen ved hjælp af
useCallback. Hvis funktionen genoprettes, kan børnekomponenten re-rendere, selvom dens andre props ikke har ændret sig. Dette kan være en betydelig flaskehals for ydeevnen. - Forbedring af ydeevne: Ved at forhindre re-renders forbedrer
useCallbackden overordnede ydeevne af din applikation, især i scenarier med hyppigt re-renderende forældrekomponenter og komplekse børnekomponenter. Dette gælder især i applikationer, der håndterer store datasæt eller hyppige brugerinteraktioner. - Optimering af Custom Hooks:
useCallbackbruges ofte i custom hooks til at moise funktioner, der returneres af hook'en. Dette sikrer, at funktionerne ikke ændrer sig, medmindre deres afhængigheder ændrer sig, hvilket hjælper med at forhindre unødvendige re-renders i komponenter, der bruger disse custom hooks. - Forbedret stabilitet og forudsigelighed: Ved at kontrollere, hvornår funktioner oprettes, kan
useCallbackbidrage til en mere forudsigelig opførsel i din applikation, hvilket reducerer chancerne for uventede bivirkninger forårsaget af hyppigt skiftende funktioner. Dette er nyttigt til fejlfinding og vedligeholdelse af applikationen.
Hvordan useCallback virker: Et dybere dyk
Når useCallback kaldes, tjekker React, om nogen af afhængighederne i afhængighedslisten har ændret sig siden sidste rendering. Hvis afhængighederne ikke har ændret sig, returnerer useCallback den memoisede funktion fra den forrige rendering. Hvis nogen af afhængighederne har ændret sig, genopretter useCallback funktionen og returnerer den nye funktion.
Tænk på det sådan her: Forestil dig, at du har en speciel type automat, der udleverer funktioner. Du giver maskinen en liste over ingredienser (afhængigheder). Hvis disse ingredienser ikke har ændret sig, giver maskinen dig den samme funktion, som du fik sidste gang. Hvis en ingrediens ændrer sig, opretter maskinen en ny funktion.
Eksempel:
import React, { useCallback, useState } from 'react';
function ChildComponent({ onClick }) {
console.log('ChildComponent re-rendered');
return (
);
}
function ParentComponent() {
const [count, setCount] = useState(0);
// Without useCallback - this will create a new function on every render!
// const handleClick = () => {
// setCount(count + 1);
// };
// With useCallback - the function only re-creates when 'setCount' changes
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]); // 'count' is the dependency
console.log('ParentComponent re-rendered');
return (
Count: {count}
);
}
export default ParentComponent;
I dette eksempel, uden useCallback, ville handleClick være en ny funktion ved hver rendering af ParentComponent. Dette ville få ChildComponent til at re-rendere hver gang ParentComponent re-renderer, selvom klikhåndteringen i sig selv ikke ændrede sig. Med useCallback ændrer handleClick sig kun, når afhængighederne ændrer sig. I dette tilfælde er afhængigheden count, som ændrer sig, når vi øger tælleren.
Hvornår skal man bruge useCallback: Bedste praksis
Selvom useCallback kan være et kraftfuldt værktøj, er det vigtigt at bruge det strategisk for at undgå overoptimering og unødvendig kompleksitet. Her er en guide til, hvornår og hvornår man ikke skal bruge det:
- Hvornår skal man bruge det:
- Når funktioner sendes som props til memoisede komponenter: Dette er den mest almindelige og afgørende anvendelse. Hvis du sender en funktion som en prop til en komponent, der er pakket ind i
React.memo(eller brugeruseMemotil memoisering), *skal* du brugeuseCallbackfor at forhindre børnekomponenten i at re-rendere unødvendigt. Dette er især vigtigt, hvis børnekomponentens re-rendering er dyr. - Optimering af Custom Hooks: Memoisering af funktioner inden i custom hooks for at forhindre deres genoprettelse, medmindre afhængighederne ændrer sig.
- Ydeevnekritiske sektioner: I sektioner af din applikation, hvor ydeevnen er absolut kritisk (f.eks. i loops, der renderer mange komponenter), kan brug af
useCallbackforbedre effektiviteten markant. - Funktioner brugt i hændelseshåndteringer, der kan udløse re-renders: Hvis en funktion, der sendes til en hændelseshåndtering, direkte påvirker tilstandsændringer, der kan udløse en re-render, hjælper
useCallbackmed at sikre, at funktionen ikke genoprettes, og dermed at komponenten ikke re-renderes unødigt. - Hvornår skal man IKKE bruge det:
- Simple hændelseshåndteringer: For simple hændelseshåndteringer, der ikke direkte påvirker ydeevnen eller interagerer med memoisede børnekomponenter, kan brugen af
useCallbacktilføje unødvendig kompleksitet. Det er bedst at evaluere den faktiske effekt, før du bruger det. - Funktioner, der ikke sendes som props: Hvis en funktion kun bruges inden for en komponents scope og ikke sendes til en børnekomponent eller bruges på en måde, der udløser re-renders, er der normalt ingen grund til at moise den.
- Overforbrug: Overforbrug af
useCallbackkan føre til kode, der er sværere at læse og forstå. Overvej altid afvejningen mellem ydeevnefordele og kodens læsbarhed. At profilere din applikation for at finde reelle ydeevneflaskehalse er ofte det første skridt.
Forståelse af afhængigheder
Afhængighedslisten er afgørende for, hvordan useCallback fungerer. Den fortæller React, hvornår den memoisede funktion skal genoprettes. Forkert specificering af afhængigheder kan føre til uventet adfærd eller endda fejl.
- Inkluder alle afhængigheder: Sørg for at inkludere *alle* variabler, der bruges inde i den memoisede funktion, i afhængighedslisten. Dette inkluderer tilstandsvariabler, props og alle andre værdier, som funktionen afhænger af. Manglende afhængigheder kan føre til forældede closures, hvor funktionen bruger forældede værdier, hvilket forårsager uforudsigelige resultater. Reacts linter vil ofte advare dig om manglende afhængigheder.
- Undgå unødvendige afhængigheder: Inkluder ikke afhængigheder, som funktionen faktisk ikke bruger. Dette kan føre til unødvendig genoprettelse af funktionen.
- Afhængigheder og tilstandsopdateringer: Når en afhængighed ændres, genoprettes den memoisede funktion. Sørg for, at du forstår, hvordan dine tilstandsopdateringer fungerer, og hvordan de relaterer sig til dine afhængigheder.
- Eksempel:
import React, { useCallback, useState } from 'react';
function MyComponent({ prop1 }) {
const [stateValue, setStateValue] = useState(0);
const handleClick = useCallback(() => {
// Include all dependencies: prop1 and stateValue
console.log('prop1: ', prop1, 'stateValue: ', stateValue);
setStateValue(stateValue + 1);
}, [prop1, stateValue]); // Correct dependency array
return ;
}
I dette eksempel, hvis du udelod prop1 fra afhængighedslisten, ville funktionen altid bruge den oprindelige værdi af prop1, hvilket sandsynligvis ikke er, hvad du ønsker.
useCallback vs. useMemo: Hvad er forskellen?
Både useCallback og useMemo er React Hooks, der bruges til memoisering, men de tjener forskellige formål:
useCallback: Returnerer en memoised *funktion*. Det bruges til at optimere funktioner ved at forhindre dem i at blive genoprettet, medmindre deres afhængigheder ændrer sig. Primært designet til ydeevneoptimering relateret til funktionsreferencer og re-renders af børnekomponenter.useMemo: Returnerer en memoised *værdi*. Det bruges til at moise resultatet af en beregning. Dette kan bruges til at undgå at genkøre dyre beregninger ved hver rendering, især dem, hvis output ikke behøver at være en funktion.
Hvornår skal man vælge:
- Brug
useCallback, når du vil moise en funktion. - Brug
useMemo, når du vil moise en beregnet værdi (som et objekt, en liste eller en primitiv værdi).
Eksempel med useMemo:
import React, { useMemo, useState } from 'react';
function MyComponent({ items }) {
const [filter, setFilter] = useState('');
// Memoize the filtered items - an array is the result
const filteredItems = useMemo(() => {
return items.filter(item => item.includes(filter));
}, [items, filter]);
return (
setFilter(e.target.value)} />
{filteredItems.map(item => (
- {item}
))}
);
}
I dette eksempel memoiserer useMemo filteredItems-listen, som er resultatet af filtreringsoperationen. Den genberegner kun listen, når enten items eller filter ændres. Dette forhindrer listen i at re-rendere unødvendigt, når andre dele af komponenten ændres.
React.memo og useCallback: En stærk kombination
React.memo er en higher-order component (HOC), der memoiserer en funktionel komponent. Det forhindrer re-renders af komponenten, hvis dens props ikke har ændret sig. Når det kombineres med useCallback, får du kraftfulde optimeringsmuligheder.
- Hvordan det virker:
React.memoudfører en overfladisk sammenligning af de props, der sendes til en komponent. Hvis props'ene er de samme (ifølge en overfladisk sammenligning), vil komponenten ikke re-rendere. Det er her,useCallbackkommer ind i billedet: ved at moise de funktioner, der sendes som props, sikrer du, at funktionerne ikke ændrer sig, medmindre afhængighederne ændrer sig. Dette giverReact.memomulighed for effektivt at forhindre re-renders af den memoisede komponent. - Eksempel:
import React, { useCallback } from 'react';
// Memoized child component
const ChildComponent = React.memo(({ onClick, text }) => {
console.log('ChildComponent re-rendered');
return (
);
});
function ParentComponent() {
const [count, setCount] = React.useState(0);
const handleClick = useCallback(() => {
setCount(count + 1);
}, [count]);
return (
Count: {count}
);
}
I dette eksempel er ChildComponent memoised med React.memo. onClick-prop'en er memoised ved hjælp af useCallback. Denne opsætning sikrer, at ChildComponent kun re-renderer, når selve handleClick-funktionen genoprettes (hvilket kun sker, når count ændres), og når text-prop'en ændres.
Avancerede teknikker og overvejelser
Ud over det grundlæggende er der et par avancerede teknikker og overvejelser, man skal huske på, når man bruger useCallback:
- Brugerdefineret sammenligningslogik med
React.memo: SelvomReact.memosom standard udfører en overfladisk sammenligning af props, kan du angive et andet argument, en sammenligningsfunktion, for at tilpasse prop-sammenligningen. Dette giver en mere finkornet kontrol over, hvornår en komponent re-renderer. Dette er nyttigt, hvis dine props er komplekse objekter, der kræver en dyb sammenligning. - Profilerings- og ydeevneværktøjer: Brug React DevTools og browserens profileringsværktøjer til at identificere ydeevneflaskehalse i din applikation. Dette kan hjælpe dig med at finde de områder, hvor
useCallbackog andre optimeringsteknikker kan give den største fordel. Værktøjer som React Profiler i Chrome DevTools kan visuelt vise dig, hvilke komponenter der re-renderer og hvorfor. - Undgå for tidlig optimering: Begynd ikke at bruge
useCallbackoveralt i din applikation. Først skal du profilere din applikation for at identificere ydeevneflaskehalse. Fokuser derefter på at optimere de komponenter, der forårsager flest problemer. For tidlig optimering kan føre til mere kompleks kode uden betydelige ydeevneforbedringer. - Overvej alternativer: I nogle tilfælde kan andre teknikker som code splitting, lazy loading og virtualisering være mere passende til at forbedre ydeevnen end at bruge
useCallback. Overvej den overordnede arkitektur af din applikation, når du træffer optimeringsbeslutninger. - Opdatering af afhængigheder: Når en afhængighed ændres, genoprettes den memoisede funktion. Dette kan føre til ydeevneproblemer, hvis funktionen udfører dyre operationer. Overvej omhyggeligt virkningen af dine afhængigheder, og hvor ofte de ændrer sig. Nogle gange kan det være mere effektivt at gentænke dit komponentdesign eller bruge en anden tilgang.
Eksempler fra den virkelige verden og globale applikationer
useCallback bruges i vid udstrækning i React-applikationer af alle størrelser, fra små personlige projekter til store virksomhedsapplikationer. Her er et par eksempler fra den virkelige verden og hvordan useCallback anvendes:
- E-handelsplatforme: I e-handelsapplikationer kan
useCallbackbruges til at optimere ydeevnen af produktlistekomponenter. Når en bruger interagerer med produktlisten (f.eks. filtrering, sortering), skal re-renders være effektive for at opretholde en glat brugeroplevelse. Memoisering af hændelseshåndteringsfunktioner (som at tilføje en vare til kurven), der sendes til børnekomponenter, sikrer, at disse komponenter ikke re-renderes unødigt. - Sociale medie-applikationer: Sociale medie-platforme har ofte komplekse brugergrænseflader med adskillige komponenter.
useCallbackkan optimere komponenter, der viser brugerfeeds, kommentarsektioner og andre interaktive elementer. Forestil dig en komponent, der viser en liste over kommentarer. Ved at moise `likeComment`-funktionen kan du forhindre hele kommentarsektionen i at re-rendere, hver gang en bruger liker en kommentar. - Interaktiv datavisualisering: I applikationer, der viser store datasæt og visualiseringer, kan
useCallbackvære et nøgleværktøj til at opretholde responsivitet. Optimering af ydeevnen af hændelseshåndteringer, der bruges til at interagere med visualiseringen (f.eks. zoom, panorering, valg af datapunkter), forhindrer re-rendering af komponenter, der ikke er direkte påvirket af interaktionen. For eksempel i finansielle dashboards eller videnskabelige dataanalyseværktøjer. - Internationale applikationer (Lokalisering og globalisering): I applikationer, der understøtter flere sprog (f.eks. oversættelsesapps eller platforme med internationale brugerbaser), kan
useCallbackbruges sammen med lokaliseringsbiblioteker for at forhindre unødvendige re-renders, når sproget ændres. Ved at moise funktioner relateret til hentning af oversatte strenge eller formatering af datoer og tal kan du sikre, at kun de berørte komponenter opdateres, når sprogindstillingen ændres. Overvej en global bankapplikation, der viser kontosaldi i forskellige valutaer. Hvis valutaen ændres, vil du kun re-rendere den komponent, der viser saldoen i den nye valuta, og ikke hele applikationen. - Brugergodkendelses- og autorisationssystemer: Applikationer med brugergodkendelse (på tværs af alle typer lande, fra USA til Indien til Japan og mange flere!) bruger ofte komponenter, der administrerer brugersessioner og roller. Brug af
useCallbacktil at moise funktioner relateret til login, logout og opdatering af brugerrettigheder sikrer, at brugergrænsefladen reagerer effektivt. Når en bruger logger ind, eller deres rolle ændres, behøver kun de berørte komponenter at re-rendere.
Konklusion: Mestring af useCallback for effektiv React-udvikling
useCallback er et afgørende værktøj for React-udviklere, der ønsker at optimere deres applikationer. Ved at forstå dens formål, fordele og hvordan man bruger den effektivt, kan du markant forbedre ydeevnen af dine komponenter, reducere unødvendige re-renders og skabe en glattere brugeroplevelse. Husk at bruge det strategisk, profilere din applikation for at identificere flaskehalse og kombinere det med andre optimeringsteknikker som React.memo og useMemo for at bygge effektive og vedligeholdelsesvenlige React-applikationer.
Ved at følge de bedste praksisser og eksempler, der er beskrevet i dette blogindlæg, vil du være godt rustet til at udnytte kraften i useCallback og skrive højtydende React-applikationer til et globalt publikum.