in React

React.useMemo()

UseMemo consente di memorizzare valori all’interno di un componente React. Supponiamo di aver realizzato un semplicente componente che consente di filtrare una lista di utenti:

import React from 'react';
 
const users = [
  { id: 'a', name: 'Robin' },
  { id: 'b', name: 'Dennis' },
];
 
const App = () => {
  const [text, setText] = React.useState('');
  const [search, setSearch] = React.useState('');
 
  const handleText = (event) => {
    setText(event.target.value);
  };
 
  const handleSearch = () => {
    setSearch(text);
  };
 
  const filteredUsers = users.filter((user) => {
    console.log('Filter function is running ...');
    return user.name.toLowerCase().includes(search.toLowerCase());
  });
 
  return (
    <div>
      <input type="text" value={text} onChange={handleText} />
      <button type="button" onClick={handleSearch}>
        Search
      </button>
 
      <List list={filteredUsers} />
    </div>
  );
};
 
const List = ({ list }) => {
  return (
    <ul>
      {list.map((item) => (
        <ListItem key={item.id} item={item} />
      ))}
    </ul>
  );
};
 
const ListItem = ({ item }) => {
  return <li>{item.name}</li>;
};
 
export default App;

La UI dell’applicazione è composta da una casella di input (che gestisce il cambio di carattere salvando il valore all’interno di una variabile chiamata text) ed un pulsante per scatenare la ricerca tramite setSearch.

Il problema piuttosto evidente è che la ricerca avviene solo quando viene premuto esplicitamente il pulsante search da parte dell’utente.

Inoltre, filteredUsers  non cambia quando un utente scrive qualcosa all’interno della casella di input, perchè cambia soltanto quando un utente effettua il click sul pulsante di ricerca.

Nonostante tutto la funzione filteredUsed viene eseguita ogni volta che viene modificato il testo all’interno della casella di input.

Ovviamente questo comportamento non comporta particolari problemi di performance perchè i dati sono relativamente pochi, ma se avessimo a che fare con una quantità maggiore di dati (o chiamate esterne) la UI potrebbe risentirne.

React.useMemo()

Nell’articolo precedente abbiamo visto il funzionamento di React.memo che consente di memorizzare un particolare componente e renderizzarlo nuovamente solo nel caso in cui si siano verificati dei cambiamenti al suo interno.

In maniera analoga possiamo utilizzare useMemo(), un hook specifico che consente di memorizzare specifici valori, aggiornandoli soltanto in caso di cambiamenti.

Nel nostro caso possiamo memorizzare il valore di filteredUsers  e ricalcolarlo solo nel caso in cui sia verificato un cambiamento della proprietà di ricerca search.

Possiamo riscrivere la funzione filteredUsers nel seguente modo:

const filteredUsers = React.useMemo(
    () =>
      users.filter((user) => {
        console.log('Filter function is running ...');
        return user.name.toLowerCase().includes(search.toLowerCase());
      }),
    [search]
  );

In pratica abbiamo racchiuso la fuzione all’interno di un blocco React.useMemo, indicato nell’array di dipendenza quale variabile dovrà essere monitorata.

A questo punto una modifica all’interno della casella di testo non farà eseguire la funzione filteredUses, che verrà triggerata soltanto dal click del pulsante.

Potrebbe sorgere una domanda: perchè non utilizzare React.useMemo() su tutte le funzioni? Semplicemente perchè la funzione di confronto sul trigger ha un costo in termini di performance, che potrebbe essere superiore a quello dell’esecuzione della funzione stessa.