useMemo e useCallback sono due hook forniti da React che aiutano a ottimizzare le prestazioni delle applicazioni. Entrambi consentono di memorizzare calcoli pesanti o funzioni per evitare ricalcoli o ricreazioni inutili durante il re-rendering dei componenti. Tuttavia, sono utilizzati in scenari leggermente diversi. Ecco un approfondimento tecnico su entrambi, con esempi in TypeScript.
useMemo
L'hook useMemo è utilizzato per memorizzare il risultato di una funzione costosa dal punto di vista computazionale. Il valore memorizzato verrà ricomputato solo se una delle sue dipendenze cambia. Questo è utile quando abbiamo operazioni costose che non vogliamo eseguire ad ogni render.
Sintassi di useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
- Primo argomento: una funzione che ritorna il valore che si desidera memorizzare.
- Secondo argomento: un array di dipendenze. La funzione viene rieseguita solo se una delle dipendenze cambia.
Punti chiave su useMemo
- Utilizzato per ottimizzare le prestazioni memorizzando valori costosi da ricomputare.
- Ricalcola il valore solo quando cambiano le dipendenze. -Previene operazioni costose ad ogni render.
Esempio di utilizzo di useMemo
import React, { useState, useMemo } from 'react';
const ExampleUseMemo: React.FC = () => {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(false);
// Utilizziamo `useMemo` per memorizzare il risultato di un calcolo fittiziamente "costoso"
// basato sul `count`. Questo valore verrà ricalcolato solo quando `count` cambia,
// evitando di eseguire il calcolo ad ogni render.
const expensiveCalculation = useMemo(() => {
console.log("Esecuzione del calcolo costoso...");
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sin(count) + Math.cos(count);
}
return result;
}, [count]);
return (
<div>
Count: {count} - Calcolo Costoso: {expensiveCalculation}
<button onClick={() => setCount((c) => c + 1)}>Incrementa</button>
<button onClick={() => setOtherState(!otherState)}>Cambia Altro Stato</button>
</div>
);
};
useCallback
L'hook useCallback memorizza una funzione callback tra i render. È utile quando una funzione viene passata come prop a componenti figli e non si desidera che venga ricreata ad ogni render, cosa che potrebbe portare a render inutili dei componenti figli.
Sintassi di useCallback
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
Punti chiave su useCallback
- Evita la ricreazione di funzioni callback tra i render.
- Utile per passare callbacks a componenti ottimizzati che dipendono dall'uguaglianza delle props per evitare render inutili.
- Aiuta a mantenere stabili le prop dei componenti figli.
Esempio di utilizzo di useCallback
import React, { useState, useCallback, useEffect } from 'react';
// Componente figlio che riceve la funzione `increment` come prop
const ChildComponent: React.FC<{ increment: () => void }> = ({ increment }) => {
useEffect(() => {
// Logga un messaggio ogni volta che il componente riceve una nuova istanza della funzione `increment`
console.log('La funzione increment è cambiata.');
}, [increment]);
return <button onClick={increment}>Incrementa da Figlio</button>;
};
const ExampleUseCallback: React.FC = () => {
const [count, setCount] = useState(0);
const [otherState, setOtherState] = useState(false);
// Utilizziamo `useCallback` per assicurarci che la funzione `increment` non venga
// ricreata ad ogni render a meno che `count` non cambi.
const increment = useCallback(() => {
setCount((c) => c + 1);
}, [count]);
return (
<div>
Count: {count}
<button onClick={increment}>Incrementa</button>
<button onClick={() => setOtherState(!otherState)}>Cambia Altro Stato</button>
<ChildComponent increment={increment} />
</div>
);
};
In questo esempio, ogni volta che modifichi otherState cliccando su "Cambia Altro Stato", il componente ExampleUseCallback viene ri-renderizzato. Tuttavia, grazie all'uso di useCallback, la funzione increment passata a ChildComponent non viene ricreata se count non cambia. Questo significa che non vedrai il log "La funzione increment è cambiata." quando modifichi otherState, indicando che increment mantiene lo stesso riferimento tra i render.
Al contrario, se rimuovi useCallback oppure cambi le sue dipendenze in modo che la funzione venga ricreata più frequentemente, noterai che il log appare ogni volta che il componente padre viene ri-renderizzato, dimostrando che senza useCallback, o con un uso improprio, la funzione increment verrebbe ricreata inutilmente, potenzialmente portando a performance peggiori in scenari con componenti figli complessi o con props che dipendono dall'uguaglianza di riferimento.
Conclusioni
L'utilizzo appropriato di useMemo e useCallback può portare a miglioramenti significativi nelle prestazioni delle applicazioni React, specialmente in quelle con una grande quantità di dati o logica computazionalmente intensiva. Tuttavia, è importante utilizzarli solo quando necessario, poiché un uso eccessivo può avere l'effetto opposto, introducendo complessità aggiuntiva e potenzialmente degradando le prestazioni a causa del costo di memorizzazione.