useCallback se utiliza para retornar una función memoizada. A diferencia de useMemo que memoiza un valor, este hook memoiza una función como tal.
Para saber cuándo y cómo usar este hook, debemos entender qué problema busca solucionar.
Igualdad de funciones
Notemos la siguiente función en javascript.
function factory() {
return (a, b) => a + b;
}
Esta función retorna una nueva función que recibe 2 parámetros y devuelve el cálculo de dichos parámetros.
Ahora ejecutemos factory y veamos los resultados.
const sum1 = factory();
const sum2 = factory();
sum1(1, 2); // retorna 3
sum2(1, 2); // retorna 3
Todo normal, comportamiento esperado de acuerdo con lo que explicamos previamente.
Pero, ¿qué pasa si hacemos una revisión de igualdad de sum1 con sum2 ?
sum1 === sum2; // => false
sum1 === sum1; // => true
si sum1 y sum2 ejecutan el mismo proceso y son productos de la misma función factory, ¿por qué la revisión de igualdad da falso?
En JS las funciones también son objetos. Las funciones, al igual que muchos otros tipos de dato, provienen del prototipo Object (también conocido como la clase Object) por lo tanto, podemos decir que sum1 y sum2 en realidad son 2 objetos completamente diferentes y por eso la revisión de igualdad nos arroja falso.
Ahora bien, escalemos este principio a React
useCallback entra en escena
Notemos el siguiente componente
import React from 'react';
function MyComponent() {
const handleClick = () => {
console.log('Clicked!');
};
// resto del código.
}
Respecto al código anterior es acertado decir que, cada vez que MyComponent se renderiza handleClick será un nuevo objeto function.
Esto no es un problema en sí, las arrow functions no generan un impacto grande en términos de memoria. Si necesitamos unas cuantas funciones similares en forma a handleClick, lo más probable es que no nos veamos afectados por el hecho de que se crean nuevas instancias de las funciones cada vez que el componente se renderiza.
Sin embargo, en algunos escenarios es necesario u óptimo, mantener la instancia de una función a través del ciclo de vida un componente, en otras palabras, utilizar la misma instancia de la función para cada renderización del componente. Y es en este escenario en el que podemos utilizar el hook useCallback
useCallback(callbackFun, deps)
deps es un arreglo de dependencias (las dependencias son variables comunes y corrientes), cada vez que se reciban las mismas dependencias, useCallback retornará la instancia de la función memoizada para dichas dependencias, de esta manera es posible mantener la instancia de la función entre diferentes renderizaciones del componente.
Cuándo sí usar useCallbak
Piensa en el siguiente caso de uso. Tienes una gran lista de registros que deseas renderizar en un componente llamado MyBigList.
import React from 'react';
function MyBigList({ term, onItemClick }) {
// código para obtener los items desde una API
const map = item => <div onClick={onItemClick}>{item}</div>;
return <div>{items.map(map)}</div>;
}
export default React.memo(MyBigList);
Para este código queremos enfocarnos principalmente, en la línea 6 y 11. Como puedes ver, para cada item estaremos utilizando un handler para manipular el evento click de cada elemento de la lista de registros. Finalmente usamos React.memo para optimizar la renderización de nuestra lista ya que tiene muchos items
Ahora vamos a incluir este componente en un componente padre
import React, { useCallback } from 'react';
export default function MyParent({ term }) {
const onItemClick = useCallback(event => {
console.log('You clicked ', event.currentTarget);
}, [term]);
return (
<MyBigList
term={term}
onItemClick={onItemClick}
/>
);
}
Como puedes observar, onItemClick es una función memoizada mediante useCallback, cada vez que la prop term tenga el mismo valor entre renderizaciones, onItemClick será una instancia existente de la función, no se creará una instancia nueva.
¿Por qué es esto cool? Recuerda que nuestra lista MyBigList utiliza React.memo, por lo tanto si queremos optimizar nuestra renderización, es fundamental que las props no cambien innecesariamente a través del ciclo de vida de la app, useCallback logra precisamente ese propósito, nuestra función onItemClick no cambiará en tanto la propiedad term no cambie y voulá, estamos usando useCallback de la forma adecuada, logrando que la renderización de nuestra lista sea eficiente.
Cabe señalara que este enfoque no es siempre el mejor, por ejemplo si no es una lista tan extensa, si los datos cambian con mucha frecuencia, tal vez no sea tan aplicable la memoización.
****
Top comments (0)