Frontend

useMemo e useCallback per migliorare le performance

Autore

Manuel Ricci

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

1const 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

1import React, { useState, useMemo } from 'react';
2
3const ExampleUseMemo: React.FC = () => {
4  const [count, setCount] = useState(0);
5  const [otherState, setOtherState] = useState(false);
6
7  // Utilizziamo `useMemo` per memorizzare il risultato di un calcolo fittiziamente "costoso"
8  // basato sul `count`. Questo valore verrà ricalcolato solo quando `count` cambia,
9  // evitando di eseguire il calcolo ad ogni render.
10  const expensiveCalculation = useMemo(() => {
11    console.log("Esecuzione del calcolo costoso...");
12    let result = 0;
13    for (let i = 0; i < 1000000; i++) {
14      result += Math.sin(count) + Math.cos(count);
15    }
16    return result;
17  }, [count]);
18
19  return (
20    <div>
21      Count: {count} - Calcolo Costoso: {expensiveCalculation}
22      <button onClick={() => setCount((c) => c + 1)}>Incrementa</button>
23      <button onClick={() => setOtherState(!otherState)}>Cambia Altro Stato</button>
24    </div>
25  );
26};

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

1const memoizedCallback = useCallback(() => {
2  doSomething(a, b);
3}, [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

1import React, { useState, useCallback, useEffect } from 'react';
2
3// Componente figlio che riceve la funzione `increment` come prop
4const ChildComponent: React.FC<{ increment: () => void }> = ({ increment }) => {
5  useEffect(() => {
6    // Logga un messaggio ogni volta che il componente riceve una nuova istanza della funzione `increment`
7    console.log('La funzione increment è cambiata.');
8  }, [increment]);
9
10  return <button onClick={increment}>Incrementa da Figlio</button>;
11};
12
13const ExampleUseCallback: React.FC = () => {
14  const [count, setCount] = useState(0);
15  const [otherState, setOtherState] = useState(false);
16
17  // Utilizziamo `useCallback` per assicurarci che la funzione `increment` non venga
18  // ricreata ad ogni render a meno che `count` non cambi.
19  const increment = useCallback(() => {
20    setCount((c) => c + 1);
21  }, [count]);
22
23  return (
24    <div>
25      Count: {count}
26      <button onClick={increment}>Incrementa</button>
27      <button onClick={() => setOtherState(!otherState)}>Cambia Altro Stato</button>
28      <ChildComponent increment={increment} />
29    </div>
30  );
31};

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.

Caricamento...

Diventiamo amici di penna? ✒️

Iscriviti alla newsletter per ricevere una mail ogni paio di settimane con le ultime novità, gli ultimi approfondimenti e gli ultimi corsi gratuiti puubblicati. Ogni tanto potrei scrivere qualcosa di commerciale, ma solo se mi autorizzi, altrimenti non ti disturberò oltre.

Se non ti piace la newsletter ti ricordo la community su Discord, dove puoi chiedere aiuto, fare domande e condividere le tue esperienze (ma soprattutto scambiare due meme con me). Ti aspetto!

Ho in previsione di mandarti una newsletter ogni due settimane e una commerciale quando capita.