Hook de optimización de rendimiento useCallback en React
En esta lección consideraremos el siguiente
hook para optimización de rendimiento
useCallback.
El hook useCallback es similar al API useMemo,
la diferencia consiste en que el primero
almacena en caché un valor entre momentos de repintado
de pantalla, y el segundo - un callback.
Esto nos permite no reiniciar funciones que consumen
muchos recursos cuando no es necesario y puede
usarse al
pasar una función
a componentes hijos.
Profundicemos más con un ejemplo.
Para empezar creemos un componente App
y establezcamos en él un estado num:
const [num, setNum] = useState(0);
Que tengamos un botón, al hacer clic
en el cual num aumenta
en 1, y un párrafo, en el cual
mostraremos el valor de num:
return (
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
</div>
);
Y ahora, supongamos que en
App se muestra además alguna lista
con elementos, la cual complementaremos
al presionar otro botón. Para almacenar
los elementos de esta lista crearemos
un estado items:
const [items, setItems] = useState([]);
Y luego escribiremos la función addItem
para añadirlos:
function addItem() {
setItems([...items, 'new item']);
}
Ahora escribamos el código para mostrar
los elementos de la lista y lo saquemos a un componente
hijo Items, que mediante
props recibirá el array de elementos
y la función para añadirlos. No olvidemos
añadir una salida a la consola, para ver
cuándo nuestro Items se
repintará:
function Items({ items, addItem }) {
const result = items.map((item, index) => {
return <p key={index}>{item}</p>;
});
console.log('Items render');
return (
<div>
<h3>Our items</h3>
{result}
<button onClick={addItem}>add item</button>
</div>
);
}
export default Items;
Coloquemos Items al final del componente
App y le pasemos el array
items y la función para añadir
elementos addItem:
return (
<>
<div>
<button onClick={() => setNum(num + 1)}>click</button>
<p>clicks: {num}</p>
<br />
</div>
<Items items={items} addItem={addItem} />
</>
);
Y ahora hagamos clic en los botones
y comprobemos que num crece y
se añaden nuevos elementos a la lista.
Y al abrir la consola, veremos que
nuestra lista se repinta cada
vez, incluso si hacemos clic en el botón
que incrementa num.
Si tenemos una lista pequeña, todo está
bien, pero ¿y si se supone que será
voluminosa y hay muchas cosas más?
No hay problema - dirán ustedes, porque en la lección
pasada consideramos el API memo,
para justamente evitar repintados innecesarios
del componente.
Así que envolvamos nuestro componente
Items en memo y listo.
Por cierto, esto se puede hacer directamente
al exportar Items:
export default memo(Items);
No olvidemos importar memo:
import { memo } from 'react';
Y ahora abramos la consola y hagamos clic
en los botones. ¡Todos los esfuerzos en vano!
Memorizamos el componente, pero al presionar
el botón 'click' el componente
Items de todas formas
se repinta cada vez.
El asunto es que cuando el componente
padre se repinta, sus funciones
se recrean de nuevo - esto afecta a nuestra
función addItem, que pasamos a
Items.
Justo en este momento nos ayudará el hook
useCallback. Apliquémoslo.
Para empezar importémoslo en
App:
import { useCallback } from 'react';
Luego rehagamos la simple declaración de la función
addItem en una
Expresión de Función, indiquemos como
primer parámetro para useCallback
nuestra función en forma de callback. El segundo
parámetro entre corchetes serán
las dependencias - todas las variables reactivas
que participan en la función, en nuestro caso
es el array items:
const addItem = useCallback(() => {
setItems(() => [...items, 'New item']);
}, [items]);
¡Listo! De este modo hemos almacenado en caché
la función. Hacemos clic de nuevo en los botones y
vemos que ahora al presionar el botón
'click' nuestro componente hijo no
se repinta.
Cree un componente App, coloque
en él un párrafo con texto. Cree
un estado con el valor inicial 'texto'
y muéstrelo en el párrafo. Que al hacer clic
en el párrafo se le añada al final del texto
un signo de exclamación.
Cree un componente hijo Products,
en el cual tendrá un botón para añadir
un nuevo producto. Colóquelo en App.
En el componente padre cree un estado
con un array de productos y una función para añadir
un nuevo producto. Páselos como
props al componente hijo, muestre en él
el array pasado en forma de lista ul.
En Products muestre en la consola el texto
'products render'.
Envuelva Products en memo.
Haga clic en el párrafo y el botón. Compruebe
que al hacer clic en el párrafo el componente hijo
se repinta de todas formas.
Almacene en caché la función para añadir
productos, envolviéndola en el hook useCallback.
Haga clic en el párrafo y el botón. Compruebe
que al hacer clic en el párrafo, el componente hijo
ya no se repinta.