Utilizando los Hooks useCallback y useMemo en React.js
by Juan Carlos García
3-Mzo-2023
(2)
Suscribirme al canal:
¿Cómo están viajeros y viajeras web? Dando continuidad a este curso de gratuito de React.JS, veremos algunos Hooks con los cuales podremos mejorar el rendimiento de nuestra aplicación, aprenderemos a utilizar useCallback y useMemo y, espero te sea de utilidad, pero, como vimos en el post anterior debes ser precavido al momento de decidir utilizar o no este tipos de Hooks.
- Antes de continuar te recomiendo visitar el post done hablamos de React.memo(), ya que, en este post te dejo recomendaciones que deberías tomar en cuenta antes de memorizar tus componentes.
¡No te puedes perder las nuevas clases 🧐!
¿Que es React JSX?
Guía para principiantes de React JSX: ¿Qué es React JSX? Introducción a React JSX
(8)
Componentes y propiedades
Components and Props React.js, Manejo de propiedades y componentes en React
(4)
State y Ciclo de vida en React
State And Lifecycle: Comprendiendo el State y Ciclo de vida de los componentes en React.js
(4)
Refs React
¿Qué son las React Refs y para que sirven? Uso de las Refs y manejo del DOM en React.js
(1)
Listas y condiciones en React
Keys and List: ¿Cómo manejar las listas y condicionales en React JS?
(2)
Formularios en React
Formularios en React: ¿Cómo manejar los formularios en React.js con input controlados y no controlados?
(2)
React develepor tools
React Developer Tools: ¿Cómo analizar el comportamiento y estado de nuestros componentes en React.js?
(2)
API Context
API Context en React: ¿Cómo comunicar componentes mediante API Context en React.js?
(3)
Children en React JS
Children en React.js: Manejo avanzado de Children en React.js, composición de componentes.
(3)
PropTypes en React
PropType en React.js: ¿Cómo validar datos a través de PropType en React.js?
(2)
Hooks en React JS
Master de Hooks en React JS: ¿Qué es un Hook en React.js? Tipos, funcionamiento y características.
(1)
Hook useEffect
Hook useEffect: ¿Qué es el Hook useEffect? ¿Cómo utilizarlo correctamente y qué reglas seguir?
(5)
Hook useContext
¿Cómo utilizar el Hook useContext? Comunicación de componentes funcionales
(4)
🧐 Autoevaluación: Hook useCallback
¿Cuándo usar useCallback React.js?
¿Qué es useMemo?
¿Cuál es la diferencia entre useMemo y React.Memo?
¿Qué aprenderás de useCallback y useMemo ?
- ¿Qué es y para qué sirve useCallback?
- Aprenderás a utilizar useCallback en conjunto con React.memo
- ¿Qué es y para qué sirve useMemo?
Repositorio
- Puedes descargar el código de de esta clase aquí.
- O si deseas clona el repositorio y cambia a la rama "usecallback".
git clone https://github.com/EWebik/react-js.git
git checkout usecallback
¿Qué es y para qué sirve useCallback?
El Hook useCallback es un elemento de React que nos permite memorizar una función, por lo tanto, la lógica en su interior trabajará con los valores iniciales establecidos en el momento en que fue creada.
No obstante, como todo Hook, recibe como segundo argumento un arreglo de dependencias, con las cuales, le indicamos a React cuando es que la función debe actualizarse, veamos su declaración.
Declaración useCallback
const memoizedCallback = useCallback(
() => {
//Lógica que es afectada o afecta a las dependencias "a" y "b"
},
[a, b],
);
¿Cómo utilizar useCallback con React.memo()?
Bien, si con useCallback memorizamos una función, con React.memo lo que hacemos es memorizar un componente ,y evitar, que este renderice cuando no sea necesario.
Si en nuestra aplicación evitamos que un componente renderice cuando no sea necesario, vamos a obtener como resultado que nuestras páginas tengan un mejor rendimiento y sean más rápidas.
- No obstante, no siempre es necesario utilizar este tipo de elementos si quieres saber cuando usar y cuando no usar memorización, revisa el post de React.memo.
Antes de utilizar useCallback debemos memorizar nuestro componente, así que voy a crear un componente que renderizará una lista y posteriormente lo envolveré con el HOC React.memo.
Componente sin memorizar
const List = (props)=>{
const{list,handleDelete}=props;
return(
<div className="hijo">
<h2>{"Lista memorizada"}</h2>
<p>{"Último render: " + new Date().getMilliseconds()}</p>
{
list.map((item)=>(
<div key={item.id}>
<span style={{marginRight:10}}>
{`Id: ${item.id}, Nombre: ${item.nombre}`}
</span>
<button onClick={()=>{
handleDelete(item.id)
}}>{"Eliminar"}</button>
</div>
))
}
</div>
)
}
- El componente List, recibe dos propiedades:
- List: arreglo de datos
- HandleDelete: función que nos permite eliminar un dato o item del arreglo
- El objetivo principal de este componente es crear una lista con los datos que vienen en la propiedad List.
- Cada item o dato renderizado mostrará un botón que nos permitirá eliminar un item en particular.
- Y vamos a tener un texto con la leyenda "Ultimo render" donde imprimimos new Date().getMilliseconds() para saber si nuestro componente ha vuelto a renderizar.
Gracias por tu calificación
(2)
Memorizando el componente List con React.memo
const ListMemo = React.memo(
(props)=>{
return(
<List {...props} />
)
}
)
- Para controlar el renderizado de nuestra lista, debemos envolver a nuestro componente con el HOC React.memo, tal como lo hacemos con el nuevo componente; ListMemo.
- El componente ListMemo, solo volverá a renderizarse cuando sus propiedades cambien, no obstante, recuerda que el componente List, recibe la función handleDelete, la cual debes manejar cuidadosamente para que no interfiera con tus reglas de renderizado, esto lo veremos a continuación.
Hasta aquí no tenemos nada nuevo, hemos memorizado a List con React.memo, ahora solo hay que mandarla llamar en nuestro componente principal.
const Component = ()=>{
const [counter1, setCounter1] = useState(0);
const [list, setList] = useState([]);
const [id, setId] = useState(0);
const [update, setUpdate] = useState(0);
useEffect(()=>{
let interval = setInterval(() => {
setCounter1(counter1 + 1);
if(counter1 >= 100){
let tList = list;
tList.push({
id:id,
nombre:"Producto"+id
});
setList(tList);
setId(id + 1);
setCounter1(0);
setUpdate(!update);
}
}, 1);
return ()=>{
clearInterval(interval);
}
},[counter1]);
const handleDeleteCallback =
(id)=>{
const tList = list.filter((item)=>{
return item.id !== id
});
setList(tList);
setUpdate(!update);
}
return(
<div className="padre">
<h1>{"EWebik mejorando el rendimiento useCallback y useMemo"}</h1>
<div className="hijos">
<Counter
title={"No memorizado"}
counter={counter1}/>
</div>
<div className="hijos">
<ListMemo
list={list}
update={update}
handleDelete={handleDeleteCallback} />
</div>
</div>
)
}
Gracias por tu calificación
(2)
- Como puedes observar, en nuestro componente principal mandamos a renderizar ListMemo, el cual es nuestro componente memorizado.
- ListMemo recibe tres propiedades:
- list: arreglo de datos de la lista
- update: lo utilizamos para forzar el renderizado
- handleDelete: función que nos permite eliminar un item del arreglo
- Dentro del cuerpo de la función, tenemos un useEffect, el cual es un Hook de efecto que nos permite realizar tareas; por ejemplo, en este caso, tenemos un setInterval que nos permite ejecutar una acción o tarea cada determinado tiempo, en este caso se ejecutara cada milisegundo e incrementará el valor de la variable de estado counter1.
- Dentro de la función de intervalo tenemos una condicional "if" con la cual cada 1000 milisegundos vamos insertar un nuevo dato a la variable de estado list, con lo cual, afectaremos nuestro componente ListMemo agregando un nuevo dato a nuestra lista.
Hemos llegado a un punto muy importante, tal y como esta el código anterior, cada determinado tiempo se agregará un nuevo dato a la lista y, si ListMemo esta memorizado, el objetivo sería que solo volviera a renderizar cuando el nuevo dato sea agregado a la lista.
Lamentablemente tal como esta el código anterior, este objetivo no se cumple, si bien:
- El componte ListMemo se vuelve a renderizar cuando un dato es agregado a la lista.
- También se renderiza por cada incremente que tiene counter1, debido al proceso que esta corriente dentro de useEffect.
- Y esto ocurre, ya que, al cambiar el valor de counter1, el componente principal se actualiza y vuelve a crear todo lo que esta dentro del cuerpo de la función y renderiza todo lo que regresa desde su return.
- Entonces, si vuelve a crear todo lo que hay dentro del cuerpo de la función, también volvería a crear la función handleDeleteCallback, lo que causaría que React.memo lo tome como un cambio en las propiedades y vuelva a renderizar el componente.
Y gracias a este comportamiento nuestro memorización a través de React.memo, no sirve para nada 😭, pero, hay una forma de solucionarlo y es utilizando el Hook useCallback, con este Hook vamos a memorizar la función handleDeleteCallback, para que, aunque el componente principal vuelva a renderizarse, esta función no se vuelva a crear.
const handleDeleteCallback = useCallback(
(id)=>{
const tList = list.filter((item)=>{
return item.id !== id
});
setList(tList);
setUpdate(!update);
},[]);
- useCallback memoriza la creación de handleDeleteCallback, y todo lo que este dentro de ella, esto quiere decir, que cada vez que se ejecute tomará los datos y valores que tenían las variables list y update, al momento de ser creada.
- Si no pasamos nada en el array de dependencias, la función se creará una sola vez cuando el componente principal es montado y no volverá a actualizarse.
- Si la función no vuelve a actualizarse, tendríamos efectos negativos en algunos casos, ya que la variable list, ira creciendo, pero la función handleDeleteCallback no lo detectará y cada vez que la invoquemos, veremos cosas raras, te recomiendo ver el vídeo ya que hay ejemplifico claramente esta situación.
- Para que la función se ejecute correctamente, pasamos list o update al arreglo de dependencias, y cada vez que alguna de estas variables cambie, la función se volverá a crear y ListMemo volverá a renderizar.
const handleDeleteCallback = useCallback(
(id)=>{
const tList = list.filter((item)=>{
return item.id !== id
});
setList(tList);
setUpdate(!update);
},[list]);
Excelente, con todos estos cambios integrando useCallback, ya tendríamos al componente ListMemo memorizado y solo se volverá a renderizar, cuando sea necesario.
Solo ten en cuenta los consejos que te mencione en la clase de React.memo, ya que muchas veces el memorizar componentes no es la primera solución en la que deberías de pensar.
Bien, ahora pasemos a revisar el Hook useMemo, con el cual podremos memorizar componentes, solo que lo haremos desde el cuerpo de la función de nuestro componente principal.
Gracias por tu calificación
(2)
¿Qué es y para que sirve useMemo?
El Hook useMemo nos permite memorizar un componente de React, es similar al HOC React.memo, ya que, mucho de lo que se hace con React.memo, lo podemos hacer con este Hook.
Declaración useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
Como todo Hook, en el primer argumento recibe una función y como segundo argumento recibirá las dependencias, si recuerdas, con React.memo controlamos que vulva a renderizar siempre y cuando haya un cambio en sus propiedades.
Con useMemo vamos a controlar de una forma similar, pero lo haremos a través del array de dependencias.
¿Cómo utilizar useMemo?
Para ejemplificar el uso de este Hook vamos a retomar el componente List que vimos en la primera sección de este post y, lo que vamos hacer es memorizarlo a través de este Hook.
const listUseMemo = useMemo(
()=>
<List
list={list}
update={update}
handleDelete={handleDelete} />
,[]);
- Como puedes observar, dentro del primer argumento agregamos una función que regresa un componente, en este caso, List, no obstante, es un componente memorizado.
- Si nosotros no agregamos nada en el arreglo de dependencias, useMemo regresará el componente memorizado en el momento en que componente principal es montado y no volverá a actualizar, en nuestro caso, regresará siempre una lista vacía.
- Entonces, en la lista de dependencias agregaremos la variable de estado update, la cual cambia cada vez que se agrega un nuevo elemento a la lista, con esto, le indicamos a useMemo que debe retornar un nuevo componente memorizado, pero con las propiedades actualizadas.
const listUseMemo = useMemo(
()=>
<List
list={list}
update={update}
handleDelete={handleDelete} />
,[update]);
Otro cambio importante al utilizar useMemo, es que en muchas ocasiones no es necesario utilizar useCallback, ya que, como puedes ver en el código anterior, no estamos utilizando handleDeleteCallback, sino simplemente handleDelete, la cual es una función no memorizada.
const handleDelete =
(id)=>{
const tList = list.filter((item)=>{
return item.id !== id
});
setList(tList);
setUpdate(!update);
};
Y esto se debe a que el Hook useMemo toma todos los parámetros necesarios una sola vez, y regresa el componente memorizado, y solo actualiza, cuando hay un cambio en su arreglo de dependencias, no en las propiedades.
Aquí te dejo el código completo para useMemo
const Component = ()=>{
const [counter1, setCounter1] = useState(0);
const [list, setList] = useState([]);
const [id, setId] = useState(0);
const [update, setUpdate] = useState(0);
useEffect(()=>{
let interval = setInterval(() => {
setCounter1(counter1 + 1);
if(counter1 >= 1000){
let tList = list;
tList.push({
id:id,
nombre:"Producto"+id
});
setList(tList);
setId(id + 1);
setCounter1(0);
setUpdate(!update);
}
}, 1);
return ()=>{
clearInterval(interval);
}
},[counter1]);
const handleDelete =
(id)=>{
const tList = list.filter((item)=>{
return item.id !== id
});
setList(tList);
setUpdate(!update);
};
//useMemo
const listUseMemo = useMemo(
()=>
<List
list={list}
update={update}
handleDelete={handleDelete} />
,[update]);
return(
<div className="padre">
<h1>{"EWebik mejorando el rendimiento useCallback y useMemo"}</h1>
<div className="hijos">
<Counter
title={"No memorizado"}
counter={counter1}/>
</div>
<div className="hijos">
{
listUseMemo
}
</div>
</div>
)
}
Si eres observador, quizá te hayas dado cuenta que para agregar en nuestro marcado la variable que proviene de useMemo "listUseMemo", no lo hacemos como componente, sino como una expresión Javascript.
<div className="hijos">
{
listUseMemo
}
</div>
Diferencia entre useMemo y React.memo
Con todo lo que hemos visto en este post, podemos establecer la principal diferencia entre React.memo y useMemo:
- La principal diferencia es que React.memo actualiza siempre y cuando haya un cambio en sus propiedades, mientras que, useMemo, solo actualiza si hay un cambio en el array de dependencias.
- Otra diferencia es que React.memo es un HOC y useMemo es un Hook.
Muy bien, espero haberme podido explicar claramente, te recomiendo ver el vídeo de esta clase para que veas en acción todo lo que te he explicado, recuerda suscribirte al boletín y al canal en YouTube para que te enteres cuando suba las siguientes clases, nos vemos en el siguiente post.
Gracias por tu calificación
(2)
🧐 Autoevaluación: Hook useCallback
¿Cuándo usar useCallback React.js?
¿Qué es useMemo?
¿Cuál es la diferencia entre useMemo y React.Memo?
Juan Carlos García
Desarrollador de software / SEO
Durante años he desarrollado plataformas dedicadas al rastreo satelital y varios sitios web que se encuentran en la primera página de Google, y hoy quiero compartir contigo lo que se en tecnologías como: Node JS, PHP, C# y Bases de datos.
Si quieres apoyarme sígueme en mis redes sociales y suscríbete a mi canal de YouTube.