Învățați cum să îmblânziți utilizarea lui React Callback Hook
Publicat: 2022-04-27Nu este un secret pentru nimeni că React.js a devenit foarte popular în ultimii ani. Acum este biblioteca JavaScript aleasă de mulți dintre cei mai importanți jucători de pe internet, inclusiv Facebook și WhatsApp.
Unul dintre principalele motive pentru creșterea sa a fost introducerea cârligelor în versiunea 16.8. Cârligele React vă permit să accesați funcționalitatea React fără a scrie componente ale clasei. Acum componentele funcționale cu cârlige au devenit structura de bază a dezvoltatorilor pentru lucrul cu React.
În această postare pe blog, vom săpa mai adânc într-un cârlig specific - useCallback
- deoarece atinge o parte fundamentală a programării funcționale cunoscută sub numele de memorare. Veți ști exact cum și când să utilizați cârligul useCallback
și să profitați la maximum de capabilitățile sale de îmbunătățire a performanței.
Gata? Să ne scufundăm!
Ce este memorarea?
Memorizarea este atunci când o funcție complexă își stochează ieșirea, astfel încât data viitoare este apelată cu aceeași intrare. Este similar cu stocarea în cache, dar la un nivel mai local. Poate sări peste orice calcule complexe și poate returna rezultatul mai rapid, deoarece este deja calculat.
Acest lucru poate avea un efect semnificativ asupra alocării și performanței memoriei, iar această solicitare este ceea ce este menit să atenueze cârligul useCallback
.
Utilizarea lui ReactCallback vs useMemo
În acest moment, merită menționat că useCallback
se împerechează frumos cu un alt cârlig numit useMemo
. Le vom discuta pe amândouă, dar în această piesă ne vom concentra pe useCallback
ca subiect principal.
Diferența cheie este că useMemo
returnează o valoare memorată, în timp ce useCallback
returnează o funcție memorată. Aceasta înseamnă că useMemo
este folosit pentru stocarea unei valori calculate, în timp ce useCallback
returnează o funcție pe care o puteți apela mai târziu.
Aceste cârlige vă vor returna o versiune în cache, cu excepția cazului în care una dintre dependențele lor (de exemplu, starea sau elementele de recuzită) se schimbă.
Să aruncăm o privire la cele două funcții în acțiune:
import { useMemo, useCallback } from 'react' const values = [3, 9, 6, 4, 2, 1] // This will always return the same value, a sorted array. Once the values array changes then this will recompute. const memoizedValue = useMemo(() => values.sort(), [values]) // This will give me back a function that can be called later on. It will always return the same result unless the values array is modified. const memoizedFunction = useCallback(() => values.sort(), [values])
Fragmentul de cod de mai sus este un exemplu artificial, dar arată diferența dintre cele două apeluri inverse:
-
memoizedValue
va deveni matricea[1, 2, 3, 4, 6, 9]
. Atâta timp cât variabila values rămâne, la fel va rămânememoizedValue
și nu se va recalcula niciodată. -
memoizedFunction
va fi o funcție care va returna matricea[1, 2, 3, 4, 6, 9]
.
Ceea ce este grozav la aceste două apeluri inverse este că devin stocate în cache și rămân până când se modifică matricea de dependențe. Aceasta înseamnă că, la randare, nu vor primi gunoiul colectat.
Redare și reacție
De ce este importantă memorarea când vine vorba de React?
Are de-a face cu modul în care React vă redă componentele. React folosește un Virtual DOM stocat în memorie pentru a compara datele și a decide ce să actualizeze.
DOM-ul virtual ajută React cu performanță și menține rapid aplicația. În mod implicit, dacă orice valoare din componenta dvs. se modifică, întreaga componentă va fi redată din nou. Acest lucru face ca React să fie „reactiv” la intrarea utilizatorului și permite ecranului să se actualizeze fără a reîncărca pagina.
Nu doriți să vă redați componenta deoarece modificările nu vor afecta acea componentă. Aici este utilă memorarea prin useCallback
și useMemo
.
Când React redă din nou componenta dvs., acesta recreează și funcțiile pe care le-ați declarat în interiorul componentei dvs.
Rețineți că atunci când comparați egalitatea unei funcții cu o altă funcție, acestea vor fi întotdeauna false. Deoarece o funcție este și un obiect, se va egala doar cu ea însăși:
// these variables contain the exact same function but they are not equal const hello = () => console.log('Hello Matt') const hello2 = () => console.log('Hello Matt') hello === hello2 // false hello === hello // true
Cu alte cuvinte, atunci când React redă din nou componenta dvs., va vedea toate funcțiile care sunt declarate în componenta dvs. ca fiind funcții noi.
Acest lucru este bine de cele mai multe ori, iar funcțiile simple sunt ușor de calculat și nu vor afecta performanța. Dar celelalte ori când nu doriți ca funcția să fie văzută ca o funcție nouă, vă puteți baza pe useCallback
pentru a vă ajuta.
S-ar putea să vă gândiți: „Când nu aș vrea ca o funcție să fie văzută ca o funcție nouă?” Ei bine, există anumite cazuri când useCallback
are mai mult sens:
- Transmiteți funcția unei alte componente care este, de asemenea, memorată (
useMemo
) - Funcția dumneavoastră are o stare internă pe care trebuie să o rețină
- Funcția dvs. este o dependență de un alt cârlig, cum ar fi
useEffect
, de exemplu
Beneficiile de performanță ale React useCallback
Când useCallback
este utilizat în mod corespunzător, poate ajuta la accelerarea aplicației și la prevenirea re-rendarii componentelor dacă nu este nevoie.
Să presupunem, de exemplu, că aveți o componentă care preia o cantitate mare de date și este responsabilă pentru afișarea acestor date sub forma unei diagrame sau a unui grafic, astfel:
Să presupunem că componenta părinte pentru re-rendările componentei de vizualizare a datelor, dar elementele de recuzită sau starea modificate nu afectează acea componentă. În acest caz, probabil că nu doriți sau nu trebuie să îl redați din nou și să recărcați toate datele. Evitarea acestei re-rendare și refacere poate economisi lățimea de bandă a utilizatorului și poate oferi o experiență mai fluidă pentru utilizator.
Dezavantajele utilizării React Callback
Deși acest cârlig vă poate ajuta să îmbunătățiți performanța, vine și cu capcanele sale. Câteva lucruri de luat în considerare înainte de a utiliza useCallback
(și useMemo
) sunt:
- Colectarea gunoiului: celelalte funcții care nu sunt deja memorate vor fi aruncate de React pentru a elibera memorie.
- Alocarea memoriei: similar cu colectarea gunoiului, cu cât aveți mai multe funcții memorate, cu atât mai multă memorie va fi necesară. În plus, de fiecare dată când utilizați aceste apeluri inverse, există o grămadă de cod în interiorul React care trebuie să folosească și mai multă memorie pentru a vă oferi rezultatul stocat în cache.
- Complexitatea codului: Când începeți să încadrați funcții în aceste cârlige, creșteți imediat complexitatea codului. Acum necesită mai multă înțelegere a motivului pentru care aceste cârlige sunt utilizate și confirmarea că sunt utilizate corect.
Să fii conștient de capcanele de mai sus te poate scuti de durerea de cap de a te împiedica singur de ele. Când vă gândiți să useCallback
, asigurați-vă că beneficiile de performanță vor depăși dezavantajele.
Reacționează folosind Exemplu de apel invers
Mai jos este o configurare simplă cu o componentă Button și o componentă Counter. Contorul are două părți de stare și redă două componente Button, fiecare care va actualiza o parte separată a stării componentelor Contor.
Componenta Button are două elemente de recuzită: handleClick
și name. De fiecare dată când butonul este randat, acesta se va conecta la consolă.
import { useCallback, useState } from 'react' const Button = ({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> } const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) return ( <> {countOne} {countTwo} <Button handleClick={() => setCountOne(countOne + 1)} name="button1" /> <Button handleClick={() => setCountTwo(countTwo + 1)} name="button1" /> </> ) }
În acest exemplu, de fiecare dată când faceți clic pe oricare dintre butoane, veți vedea acest lucru în consolă:
// counter rendered // button1 rendered // button2 rendered
Acum, dacă aplicăm useCallback
funcțiilor noastre handleClick
și înfășurăm Butonul în React.memo
, putem vedea ce ne oferă useCallback
. React.memo
este similar cu useMemo
și ne permite să memorăm o componentă.
import { useCallback, useState } from 'react' const Button = React.memo(({handleClick, name}) => { console.log(`${name} rendered`) return <button onClick={handleClick}>{name}</button> }) const Counter = () => { console.log('counter rendered') const [countOne, setCountOne] = useState(0) const [countTwo, setCountTwo] = useState(0) const memoizedSetCountOne = useCallback(() => setCountOne(countOne + 1), [countOne) const memoizedSetCountTwo = useCallback(() => setCountTwo(countTwo + 1), [countTwo]) return ( <> {countOne} {countTwo} <Button handleClick={memoizedSetCountOne} name="button1" /> <Button handleClick={memoizedSetCountTwo} name="button1" /> </> ) }
Acum, când facem clic pe oricare dintre butoane, vom vedea doar butonul pe care l-am făcut clic pentru a vă conecta în consolă:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Am aplicat memorarea componentei noastre de buton, iar valorile prop care îi sunt transmise sunt văzute ca fiind egale. Cele două funcții handleClick
sunt stocate în cache și vor fi văzute ca aceeași funcție de către React până când valoarea unui element din matricea de dependențe se schimbă (de exemplu, countOne
, countTwo
).
rezumat
Oricât de cool sunt useCallback
și useMemo
, amintiți-vă că au cazuri de utilizare specifice - nu ar trebui să includeți fiecare funcție cu aceste cârlige. Dacă funcția este complexă din punct de vedere computațional, o dependență de un alt cârlig sau un suport transmis unei componente memorate sunt indicatori buni pe care ați putea dori să îi ajungeți pentru useCallback
.
Sperăm că acest articol te-a ajutat să înțelegi această funcționalitate avansată React și te-a ajutat să câștigi mai multă încredere în programarea funcțională pe parcurs!