Guida ai React Hooks — parte 3

Maico Orazio
weBeetle
Published in
5 min readDec 10, 2020

In questa ultima parte della guida ai React Hooks presenterò due hooks utili per ottimizzare le prestazioni delle app in React.

Tieni presente che React è veloce per impostazione predefinita, quindi ogni ottimizzazione delle prestazioni è necessaria nel caso in cui qualcosa inizi a sembrare lento.

Potresti chiederti perché non usarli su tutte le funzioni o perché non sono impostati in modo predefinito da React.

Internamente, ognuno di questi hooks deve confrontare le dipendenze per ogni nuovo rendering e decidere se deve ridefinire le funzioni. Spesso il calcolo per questo confronto può essere più costoso della semplice ridefinizione della funzione.

API memo di React

L’API memo di React può essere utilizzata per ottimizzare il comportamento di rendering dei componenti.

React.memo è un cosiddetto componente di ordine superiore. Se un componente si renderizza, pur non cambiando le proprie props o stato locale, puoi racchiuderlo in una chiamata a React.memo al fine di ottenere un miglioramento tramite la memorizzazione del risultato.

Prendiamo come esempio lo stesso codice utilizzato per l’hook useState nella prima parte di questa guida, dividiamolo in più componenti per renderizzare l’elenco delle cose da fare:

Se ogni componente prova a stampare un testo nella console del browser, noterai che l’istruzione di console.log viene eseguita ogni volta che digiti qualcosa nel campo di input:

const App = () => {
console.log("Render Component App");
...
};
const ListTodos = ({todos}) => {
console.log("Render Component ListTodos");
...
};
const Todo = ({todo}) => {
console.log("Render Component Todo");
...
};

questo capita perché il componente App aggiorna il suo stato e tutti i suoi componenti figli, per impostazione predefinita di React, eseguiranno nuovamente il rendering:

doo aver digitato un carattere nel campo di input
dopo aver digitato un carattere nel campo di input

il componente Todo viene renderizzato più volte per ogni elemento presente nell’elenco todos.

Questo comportamento predefinito di React può andar bene fino a quando l’app inizia a sembrare lenta. Quando ciò accade, ad esempio quando deve essere renderizzato un enorme elenco di componenti figli, puoi utilizzare l’API memo per memorizzare la funzione del componente:

const ListTodos = React.memo(({ todos }) => {
console.log("Render Component ListTodos");
return (
<ul>
{todos.map((item, i) => (
<Todo key={i} todo={item} />
))}
</ul>
);
});

const Todo = ({ todo }) => {
console.log("Render Component Todo");
return <li key={todo.id}>{todo.title}</li>;
};

In questo modo, il componente ListTodos riceve le props che precedentemente ha memorizzato e, poiché non sono cambiate, non si renderizza; indirettamente non renderizza i suoi componenti figli, per questo non è necessario memorizzare il componente Todo.

Aggiungendo una nuova attività all’elenco, il componente Todo, come evidenziato in precedenza, viene renderizzato più volte per il numero di todos. Ma sarebbe più efficiente renderizzare solo il nuovo elemento dell’elenco:

const ListTodos = React.memo(({ todos }) => {
console.log("Render Component ListTodos");
return (
<ul>
{todos.map((item, i) => (
<Todo key={i} todo={item} />
))}
</ul>
);
});

const Todo = React.memo(({ todo }) => {
console.log("Render Component Todo");
return <li key={todo.id}>{todo.title}</li>;
});

gli elementi precedenti dell’elenco rimangono gli stessi e quindi non vengono renderizzati.

Hook useMemo

L’hook useMemo può essere utilizzato per ottimizzare i costi di calcolo dei componenti.

Nota: da non confondere con l’API memo di React, useMemo è usato per memorizzare i valori, l’API memo per avvolgere i componenti React e prevenire il rendering.

Riprendiamo lo stesso codice utilizzato per l’API memo, e aggiungiamo la possibilità di filtrare le attività per nome:

Anche se filterTodos non cambia quando digitiamo qualcosa nel campo di ricerca, la funzione di callback in todo.filter viene eseguita ugualmente:

let filterTodos = todos.filter((todo) => {
console.log("esecuzione della funzione filtro");
return todo.title.toLowerCase().includes(search.toLowerCase());
});

Forse, con un gran numero di elementi, rallenteremmo l’app; pertanto possiamo utilizzare l’hook useMemo per memorizzare i valori restituiti dalla funzione ed eseguirla solo se una qualsiasi delle sue dipendenze cambia.

import React, { useState, useMemo } from "react";
...
let filterTodos = useMemo(
() =>
todos.filter((todo) => {
console.log("esecuzione della funzione filtro");
return todo.title.toLowerCase().includes(search.toLowerCase());
}),
[search]
);

Ora la funzione che filtra viene eseguita solo una volta, anche se l’utente digita più caratteri, perché non cambia la dipendenza search.

Hook useCallback

L’hook useCallback può essere utilizzato per ottimizzare il comportamento di rendering dei componenti.

Nota: da non confondere con l’hook useMemo, useCallback è usato per memorizzare le funzioni, l’hook useMemo per memorizzare i valori.

Riprendiamo l’esempio utilizzato con l’API memo e aggiungiamo la possibilità di eliminare un’attività dall’elenco:

import React, { useState, useCallback } from “react”;

Usando ciò che abbiamo visto sopra, riguardo l’API memo, abbiamo imparato come impedire che i componenti figli vengano renderizzati nuovamente quando l’utente inizia a digitare qualcosa nel campo di input per aggiungere di un nuovo elemento all’elenco delle cose da fare.

Tuttavia, in questo caso, l’API memo non risolverà il problema di renderizzare anche i componenti figli perché, se diamo un’occhiata alle props passate, notiamo che esiste un nuovo colpevole: la funziona handleRemove.

Ogni volta che il componente App viene renderizzato, la funzione handleRemove viene ridefinita, e, passandola come nuova ai componenti figli, innesca il loro rendering perché diversa dalla precedente, risultato dal confronto effettuato dall’API memo.

Come risolvere anche questo problema? Utilizzando l’hook useCallback per memorizzare la funzione, il che significa che essa verrà ridefinita solo se cambia una qualsiasi delle sue dipendenze:

import React, { useState, useCallback } from "react";
...
const handleRemove = useCallback(
(id) => {
setTodos(todos.filter((todo) => todo.id !== id));
},
[todos]
);

Se un’attività viene aggiunta o rimossa dall’elenco delle cose da fare, quindi da todos, allora la funzione viene ridefinita e di conseguenza i componenti figli vengono renderizzati.

Conclusione

Gli hooks sono stati un cambiamento importante in React che ha creato un nuovo modo di condividere la logica e aggiornare i componenti senza usare le classi. Ora abbiamo 3 modi per esprimere i componenti in React:

  • componenti funzionali
  • componenti di classe
  • componenti funzionali con gli hooks

Ci sono molti altri React Hooks, ti suggerisco di leggere la documentazione ufficiale per saperne di più.

Come per altri articoli della guida, trovate sotto i relativi repository degli esempi. Se hai domande, puoi lasciarle nella sezione commenti, sarò felice di rispondere.

Grazie per aver letto questa guida ai React Hooks, spero ti sia piaciuta, ti aiuti a comprendere meglio le basi degli Hooks e ti permetta di partire da questi esempi per creare cose nuove e sorprendenti!

esempio con hook useMemo
esempio con hook useCallback

--

--

Maico Orazio
weBeetle

Senior Web Application Developer. I'm a software engineer, a passionate coder, and a web developer. I am a fan of technology. #php #symfony #javascript #reactjs