Reagisce il rendering non necessario dell'elemento
Ho appena iniziato a imparare React e stavo mettendo insieme una piccola app che effettua chiamate a un'API di citazioni. L'API ha un endpoint che restituisce una citazione casuale. Quando l'app viene caricata inizialmente, effettua una chiamata all'API e mostra un preventivo, e c'è un pulsante che può essere cliccato per ottenere un nuovo preventivo casuale (nuova chiamata all'API).
Ho un componente radice chiamato App. Questo componente ha un componente QuoteWrap da bambino. Il componente QuoteWrap ha due figli: il pulsante utilizzato per ottenere una nuova citazione casuale e un componente Quote che mostra l'autore della citazione e la citazione stessa. Questo è il codice all'interno del componente QuoteWrap:
export default function QuoteWrap() {
const { quoteData, isLoading, fireNewCall } = useQuote();
const handleClick = () => {
fireNewCall();
};
return(
<>
<button onClick={handleClick}>Get random quote</button>
{ isLoading ?
<h2>Loading...</h2>
:
<Quote author={quoteData.author} quote={quoteData.quote} />
}
</>
);
}
useQuote () è un hook personalizzato che gestisce le chiamate all'API e restituisce 3 valori: 1- i dati, 2- se è in corso una chiamata e 3- una funzione per effettuare una chiamata all'API.
Ovviamente, ogni volta che si fa clic sul pulsante, viene eseguito nuovamente il rendering dell'intero componente QuoteWrap (come quoteData e isLoading cambiano). Ma in realtà, il pulsante non deve essere nuovamente renderizzato poiché non cambia mai.
Quindi ho pensato: ok, posso spostare il pulsante fino al componente App. Ma poi non ho accesso alla funzione fireNewCall nell'hook useQuote.
Come posso evitare che il pulsante venga nuovamente renderizzato? È anche importante in questo caso o sto diventando troppo ossessionato dai ri-rendering di React?
Grazie!
Risposte
Il tuo componente eseguirà nuovamente il rendering ogni volta che la funzione handleClick cambia, ovvero ogni volta che viene eseguito il rendering di QuoteWrap.
La soluzione è l'hook useCallback. useCallback restituirà la stessa funzione a handleClick, ogni volta che viene eseguito il rendering di QuoteWrap, a condizione che le dipendenze non siano cambiate.
https://reactjs.org/docs/hooks-reference.html#usecallback
Lo useresti in questo modo:
const handleClick = useCallback(() => {
fireNewCall();
},[fireNewCall]);
fireNewCall è la dipendenza, quindi fintanto che useQuote restituisce una funzione fireNewCall stabile, il tuo pulsante non verrà rieseguito, poiché la proprietà handleClick non è cambiata.
Penso che potresti diventare troppo ossessionato dai ri-rendering di React. Il pulsante dovrebbe essere nuovamente renderizzato perché, in qualche caso , handleClickdovrebbe essere cambiato quando viene fireNewCallmodificato. Anche se, handleClicknon sarà mai cambiato. Non è necessario pensare a un elemento di rendering.
Più o meno quello che hanno detto Benjamin e Viet: nel codice originale, è assegnata una nuova funzione per handleClick su ogni rendering. È possibile utilizzare React.useCallbackper mantenere il riferimento alla funzione originale e aggiornarlo solo quando qualcosa nell'array delle dipendenze cambia - in questo caso, è sufficiente fireNewCallentrare nell'array delle dipendenze.
Ma come dice Viet, non ti ossessionare troppo. L'utilizzo React.useCallbackpotrebbe anche rallentare il codice. Dai un'occhiata a Kent C. Dodds When to useMemo e useCallback post per ulteriori informazioni.