Imparare a domare l'uso di React Callback Hook
Pubblicato: 2022-04-27Non è un segreto che React.js sia diventato molto popolare negli ultimi anni. Ora è la libreria JavaScript preferita da molti dei giocatori più importanti di Internet, inclusi Facebook e WhatsApp.
Uno dei motivi principali della sua ascesa è stata l'introduzione degli hook nella versione 16.8. Gli hook React ti consentono di sfruttare la funzionalità di React senza scrivere componenti di classe. Ora i componenti funzionali con hook sono diventati la struttura di riferimento degli sviluppatori per lavorare con React.
In questo post del blog, approfondiremo un hook specifico, useCallback
, perché tocca una parte fondamentale della programmazione funzionale nota come memorizzazione. Saprai esattamente come e quando utilizzare l'hook useCallback
e sfruttare al meglio le sue capacità di miglioramento delle prestazioni.
Pronto? Immergiamoci!
Cos'è la memorizzazione?
La memorizzazione è quando una funzione complessa memorizza il suo output in modo che la prossima volta che venga chiamata con lo stesso input. È simile alla memorizzazione nella cache, ma a un livello più locale. Può saltare qualsiasi calcolo complesso e restituire l'output più velocemente poiché è già calcolato.
Ciò può avere un effetto significativo sull'allocazione della memoria e sulle prestazioni e tale sforzo è ciò che l'hook useCallback
dovrebbe alleviare.
React's useCallback vs useMemo
A questo punto, vale la pena ricordare che useCallback
accoppia bene con un altro hook chiamato useMemo
. Ne discuteremo entrambi, ma in questo pezzo ci concentreremo su useCallback
come argomento principale.
La differenza fondamentale è che useMemo
restituisce un valore memorizzato, mentre useCallback
restituisce una funzione memorizzata. Ciò significa che useMemo
viene utilizzato per memorizzare un valore calcolato, mentre useCallback
restituisce una funzione che puoi chiamare in seguito.
Questi hook ti restituiranno una versione memorizzata nella cache a meno che una delle loro dipendenze (es. stato o props) non cambi.
Diamo un'occhiata alle due funzioni in azione:
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])
Il frammento di codice sopra è un esempio artificiale ma mostra la differenza tra i due callback:
-
memoizedValue
diventerà l'array[1, 2, 3, 4, 6, 9]
. Finché la variabile values rimane, lo saràmemoizedValue
e non verrà mai ricalcolato. -
memoizedFunction
sarà una funzione che restituirà l'array[1, 2, 3, 4, 6, 9]
.
La cosa fantastica di questi due callback è che vengono memorizzati nella cache e rimangono in sospeso fino a quando l'array di dipendenze non cambia. Ciò significa che in un rendering non verranno raccolti i rifiuti.
twittareRendering e reazione
Perché la memorizzazione è importante quando si tratta di React?
Ha a che fare con il modo in cui React rende i tuoi componenti. React utilizza un Virtual DOM archiviato in memoria per confrontare i dati e decidere cosa aggiornare.
Il DOM virtuale aiuta a reagire con le prestazioni e mantiene veloce la tua applicazione. Per impostazione predefinita, se un qualsiasi valore nel componente cambia, l'intero componente verrà riprodotto nuovamente. Questo rende React "reattivo" all'input dell'utente e consente allo schermo di aggiornarsi senza ricaricare la pagina.
Non vuoi eseguire il rendering del tuo componente perché le modifiche non influiranno su quel componente. È qui che la memorizzazione tramite useCallback
e useMemo
torna utile.
Quando React esegue nuovamente il rendering del tuo componente, ricrea anche le funzioni che hai dichiarato all'interno del tuo componente.
Si noti che quando si confronta l'uguaglianza di una funzione con un'altra funzione, saranno sempre false. Poiché una funzione è anche un oggetto, sarà solo uguale a se stessa:
// 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
In altre parole, quando React esegue nuovamente il rendering del tuo componente, vedrà tutte le funzioni dichiarate nel tuo componente come nuove funzioni.
Questo va bene per la maggior parte del tempo e le funzioni semplici sono facili da calcolare e non influiscono sulle prestazioni. Ma le altre volte in cui non vuoi che la funzione sia vista come una nuova funzione, puoi fare affidamento su useCallback
per aiutarti.
Potresti pensare: "Quando non vorrei che una funzione fosse vista come una nuova funzione?" Bene, ci sono alcuni casi in cui useCallback
ha più senso:
- Stai passando la funzione a un altro componente anch'esso memorizzato (
useMemo
) - La tua funzione ha uno stato interno che deve ricordare
- La tua funzione è una dipendenza di un altro hook, come ad esempio
useEffect
Vantaggi prestazionali di React useCallback
Quando useCallback
viene utilizzato in modo appropriato, può aiutare a velocizzare l'applicazione e impedire ai componenti di eseguire nuovamente il rendering se non è necessario.
Diciamo, ad esempio, che hai un componente che recupera una grande quantità di dati ed è responsabile della visualizzazione di tali dati sotto forma di grafico o grafico, come questo:
Si supponga che il componente principale per il componente della visualizzazione dati venga riprodotto, ma gli oggetti di scena o lo stato modificati non influiscono su quel componente. In tal caso, probabilmente non vuoi o non devi eseguire nuovamente il rendering e recuperare tutti i dati. Evitare questo re-rendering e refetch può far risparmiare la larghezza di banda dell'utente e fornire un'esperienza utente più fluida.
Svantaggi di React useCallback
Sebbene questo gancio possa aiutarti a migliorare le prestazioni, ha anche le sue insidie. Alcune cose da considerare prima di usare useCallback
(e useMemo
) sono:
- Raccolta di rifiuti: le altre funzioni che non sono già state memorizzate verranno eliminate da React per liberare memoria.
- Allocazione della memoria: simile alla raccolta dei rifiuti, più funzioni memorizzate hai, più memoria sarà richiesta. Inoltre, ogni volta che usi questi callback, c'è un mucchio di codice all'interno di React che ha bisogno di usare ancora più memoria per fornirti l'output memorizzato nella cache.
- Complessità del codice: quando inizi a racchiudere le funzioni in questi hook, aumenti immediatamente la complessità del tuo codice. Ora richiede una maggiore comprensione del motivo per cui questi hook vengono utilizzati e la conferma che vengono utilizzati correttamente.
Essere consapevoli delle insidie di cui sopra può salvarti dal mal di testa di inciampare su di loro tu stesso. Quando si considera l'utilizzo di useCallback
, assicurarsi che i vantaggi in termini di prestazioni superino gli svantaggi.
Reagire useCallback Esempio
Di seguito è riportata una semplice configurazione con un componente Pulsante e un componente Contatore. Il contatore ha due parti di stato e visualizza due componenti Button, ciascuno che aggiornerà una parte separata dello stato dei componenti del contatore.
Il componente Button contiene due props: handleClick
e name. Ogni volta che viene eseguito il rendering del pulsante, verrà registrato sulla console.
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" /> </> ) }
In questo esempio, ogni volta che fai clic su uno dei pulsanti, vedrai questo nella console:
// counter rendered // button1 rendered // button2 rendered
Ora, se applichiamo useCallback
alle nostre funzioni handleClick
e avvolgiamo il nostro Button in React.memo
, possiamo vedere cosa ci fornisce useCallback
. React.memo
è simile a useMemo
e ci consente di memorizzare un componente.
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" /> </> ) }
Ora, quando facciamo clic su uno dei pulsanti, vedremo solo il pulsante su cui abbiamo fatto clic per accedere alla console:
// counter rendered // button1 rendered // counter rendered // button2 rendered
Abbiamo applicato la memorizzazione al nostro componente pulsante e i valori prop passati ad esso sono visti come uguali. Le due funzioni handleClick
sono memorizzate nella cache e saranno viste come la stessa funzione da React fino a quando il valore di un elemento nell'array di dipendenza non cambia (ad esempio countOne
, countTwo
).
Sommario
Per quanto siano interessanti useCallback
e useMemo
, ricorda che hanno casi d'uso specifici: non dovresti avvolgere tutte le funzioni con questi hook. Se la funzione è computazionalmente complessa, una dipendenza di un altro hook o un prop passato a un componente memorizzato sono buoni indicatori che potresti voler raggiungere per useCallback
.
Ci auguriamo che questo articolo ti abbia aiutato a comprendere questa funzionalità avanzata di React e ti abbia aiutato ad acquisire maggiore sicurezza con la programmazione funzionale lungo il percorso!