En djupare titt på de mest populära React-krokarna
Pawel Rybczynski
Software Engineer
Under många intervjuer har jag märkt att även erfarna programmerare har problem med att skilja på Hooks, för att inte tala om deras mer avancerade funktioner. Så jag kommer att försöka förklara i den här artikeln hur Hooks ska användas.
De viktigaste sakerna du behöver komma ihåg om Hooks:
de kan endast användas i funktionskomponenter - klasskomponenter har egen livscykelimplementering;
de börjar alltid med användning;
kan du använda hur många Hooks du vill, men du måste komma ihåg att användningen av dem påverkar den totala prestandan;
de måste utföras i samma ordning vid varje rendering ... men varför? Låt oss ta en titt på ett exempel:
<code>srcFunctionKomponent.js
Rad 11:5: React Kroken "useEffect" anropas villkorligt. <strong>React Krokar</strong> måste anropas i exakt samma ordning i varje komponent som renderas .eslintreact-hooks/rules-of-hooks
Som du kan se är det bara en eslint-varning, så du kan inaktivera den genom att lägga till ett kommando underifrån högst upp i Funktionskomponent
och det kommer att fungera men bara tills vi uppfyller villkoret som kör vår Hook. Det allra nästa som vi kommer att se är detta fel.
Fel som inte upptäcktes: Renderade fler krokar än under föregående rendering.
React 5
Funktionskomponent Funktionskomponent.js:11
React 12
unstable_runWithPriority scheduler.development.js:468
React 17
js index.js:7
js main.chunk.js:905
Webpack 7
react-dom.utveckling.js:15162
Varför händer detta? React förlitar sig på den ordning i vilken krokarna anropas eftersom React inte skulle veta vad som ska returneras för useEffect eftersom det inte fanns någon sådan krok i raden att kontrollera.
Kom ihåg att eslint är ett kraftfullt verktyg som hjälper oss att hitta många potentiella buggar och fel. Att inaktivera dess varningar är en farlig sak, kontrollera alltid om ignorering av varningen kan orsaka en appkrasch.
användningStatus
Du vet säkert hur det ser ut 😉.
const [värde, setValue] = useState(0);
Så du har 4 element: tillstånd (reaktivt värde), uppdateringsfunktion (setter), faktisk krok (funktion) och valfritt initialt värde. Varför returnerar den en array? För att vi kan omstrukturera den som vi vill.
Nu vill jag fokusera på det sista elementet - det initiala värdet. Det finns två sätt att skicka det initiala tillståndet:
Med hårdkodat värde eller liknande - som kommer att anropas vid varje rendering
const [värde, setValue] = useState(0);
Genom en funktionsversion. Det är verkligen till stor hjälp om vi vill köra initialtillståndet bara en gång, vid den allra första renderingen. Kanske behöver du göra en massa komplexa beräkningar för att få det initiala tillståndet? Det kommer snyggt att minska resurskostnaden, yay!
En annan sak som kan skapa buggar i appflödet: du vet säkert hur man uppdaterar ett tillstånd, eller hur?
setValue(1);
Okej... men vad händer om jag vill uppdatera statusen baserat på en tidigare status?
setValue(värde + 1);
Ja... Men nej... Tänk om du försöker anropa setter-funktionen två gånger, efter varandra? Det rekommenderade sättet att uppdatera ett tillstånd baserat på det föregående tillståndet är att använda en funktion. Det garanterar att du refererar till det tidigare tillståndet
Denna Hook tar 2 argument (det andra är valfritt) och vi använder det för att hantera bieffekter. Och beroende på vad vi skickar som ett andra argument kommer kroken att anropas på olika sätt:
utan andra argument - varje rendering
användEffekt(() => {
doSomething();
});
tom array - endast vid första renderingen
useEffect(() => {
görnågot();
}, []);
matris med beroenden - varje gång värdet i beroendematrisen ändras
Med useEffect kan vi använda något som kallas cleanup. Vad är det för något? Det är väldigt användbart, men jag tycker att det är bäst för att städa eventlyssnare. Låt oss säga att du vill skapa en händelselyssnare som beror på något tillstånd. Du vill inte lägga till en ny eventlyssnare vid varje tillståndsändring, för efter några renderingar kommer det att finnas så många lyssnare att det kommer att påverka appens prestanda. Ett bra sätt att undvika sådana saker är att använda uppstädningsfunktionen. Hur gör man det? Lägg bara till en returfunktion till useEffect.
Eftersom det är inuti useEffect Hook anropas returen beroende på beroendematrisen - vid varje rendering, endast vid den första renderingen eller när värdet i beroendematrisen ändras. Men när komponenten avmonteras kommer cleaning att anropas på det andra argumentet oavsett vad. Returnera kod anropas före den faktiska koden från Hook. Det är väldigt logiskt - först rensa den gamla, sedan skapa en ny. Eller hur?
användEffekt(() => {
// addEventListener
console.log("Lägg till");
return () => {
// ta bort händelseförvaltare
console.log("Ta bort");
};
}, [värde]);
Så först kommer du att få en ta bort meddelande, då Lägg till.
Det finns en sak att vara uppmärksam på när man använder useEffect och asynkron kod inuti den. Ta en titt på koden nedan:
Till en början ser det ok ut. Du hämtar lite data, och när datan kommer uppdaterar du tillståndet. Och här är fällan:
Ibland kommer du att få en sådan varning: Kan inte utföra en React-statusuppdatering på en omonterad komponent. Det här är inget alternativ, men det indikerar en minnesläcka i din applikation. För att åtgärda detta, avbryt alla prenumerationer och asynkrona uppgifter i en useEffect-rensningsfunktion.
Anledningen är att komponenten kan avmonteras under tiden, men appen kommer fortfarande att försöka uppdatera komponentens tillstånd efter att löftet har uppfyllts. Hur hanterar man det? Du måste kontrollera om komponenten finns.
Obs: Det finns en mycket liknande Hook => useLayoutEffect() - återuppringningen körs efter att komponenten har renderats men innan domänen uppdateras visuellt. Det är användbart när du arbetar med getBoundingClientRect(), men du bör använda useEffect som standard. Varför är det så? Eftersom det kan blockera visuella uppdateringar - när du har en komplex kod inuti din effekt Hook.
användaKontext
Vad är den till för? Delning av data utan att skicka props. Består av följande element:
I Child måste du importera kontexten och anropa useContext Hook och skicka kontexten som ett argument.
import { UserContext } från "./App";
const { namn } = useContext(UserContext);
retur <h1>Hej {namn}<>
```
Voilà. Ser coolt ut. Mestadels för att skicka globala data som teman etc. Rekommenderas inte för användning i uppgifter med mycket dynamiska ändringar.
Naturligtvis kan vi skapa en anpassad kontextleverantör och en anpassad krok för att minska boilerplate istället ... men jag kommer att ta itu med anpassade krokar i nästa artikel.
användReducer
Det gör att vi kan hantera tillståndet och återskapa när tillståndet ändras - som useState. Det liknar redux-reduceraren. Den här är bättre än useState när tillståndslogiken är mer komplicerad.
När ska man använda det? När vi vill uppnå referentiell likhet (och därmed minska antalet skapade funktioner). Den här kroken returnerar funktionen, till skillnad från useMemo som returnerar värdet.
Exempel: Skapa en funktion i den överordnade komponenten och skicka den sedan via props
Kontrollera sedan i barnkomponenten hur många gånger effekten Hook kommer att anropas efter att funktionen har lagts till i beroendematrisen:
// Barn
useEffect(() => {
console.log("getSquaredValue", getSquaredValue());
}, [getSquaredValue]);
Den kommer att logga till konsolen vid varje rendering! Även om värdena inuti getSquaredValue() funktionen ändrades inte. Men vi kan undvika detta genom att linda in den funktionen i useCallback
Det är inte neutralt när man tittar på kostnaderna för resurser - useMemo måste anropas vid varje rendering, sparar värdet i minnet och jämför (minnesoverhead),
använder Memoization - optimeringstekniken, specifik form av cachning.
Du bör endast använda den i två scenarier:
Om du vill undvika att anropa en komplex kod vid varje rendering;
Om du vill uppnå referentiell jämlikhet.
Låt oss titta lite närmare på det andra fallet. Vi vill använda useEffect med ett objekt som ett beroende. Eftersom objekt jämförs med sin referens kommer useEffect att anropas vid varje rendering. För att undvika sådana saker kan vi kombinera useEffect med useMemo för att memorera sådana objekt och sedan skicka de memorerade objekten till beroendematrisen. Kort exempel:
Objektet "hobbit" gör att beroendena för useEffect Hook (rad 49) ändras vid varje rendering. Flytta in det i useEffect callback. Alternativt kan initialiseringen av 'hobbit' paketeras i sin egen useMemo () Hook.eslintreact-hooks/exhaustive-deps
Det viktigaste: useRef utlöser inte omrendering (som useState) eftersom den inte är kopplad till renderingscykeln - den behåller samma referens mellan renderingarna.
const ref = useRef(0);
Om du vill anropa det sparade värdet måste du använda en aktuell egenskap (ref är ett objekt) ref.nuvarande
Det andra fallet där vi kan använda den här kroken är för att referera till element i HTML. Varje element har ett ref-attribut. Så vi kan hantera fokus, händelser etc.
Det tredje fallet är att vi kan använda refs för att hantera okontrollerade komponenter. Du kan läsa mer om dem i React-dokument, men i korthet ser det ut så här:
Som du kan se finns det ingen händelsehanterare, den kommer bara ihåg det inskrivna värdet. Det är utmärkt för att hantera grundläggande formulär när du bara vill läsa sparade värden när du behöver dem (som när du skickar in).
Bonus: Det är bra när du behöver komma ihåg tidigare tillståndsvärden. Du kan använda useEffect Hook för det, bara skicka tillståndet till ref.
Som du kan se är Hooks inte så uppenbara. Vi kan kombinera dem för att lösa många problem. Du kommer säkert att ha stor nytta av att studera detta ämne.
Och det finns också anpassade krokar...
Sammanfattningsvis, React krokar har revolutionerat sättet React utvecklare tillvägagångssätt byggnad webbapplikationer . Genom att tillhandahålla ett mer intuitivt och effektivt sätt att hantera tillstånd och livscykel i funktionella komponenter har krokar blivit en integrerad del av React utveckling .
Oavsett om du är en erfaren utvecklare eller precis har börjat med React är det viktigt att förstå de mest populära krokarna och deras användningsområden. Med krokar som useState, useEffect, useContext och mer, React-komponenter kan byggas med renare och mer återanvändbar kod. Dessutom är möjligheten att skapa anpassade krokar gör det möjligt för utvecklare att kapsla in och dela logik över flera komponenter, vilket främjar återanvändning av kod och modularitet. I takt med att React fortsätter att utvecklas och introducera nya funktioner kommer hooks utan tvekan att spela en central roll för att utnyttja ramverkets fulla potential.
Så oavsett om du arbetar med en liten funktionsapp eller en storskalig webbapplikation, är det viktigt att ta till sig React krokar kommer att förbättra ditt utvecklingsarbetsflöde och öppna upp en uppsjö av möjligheter för att skapa robusta och funktionsrika React-applikationer .