useCallback
useCallback
El hook useCallback
en React se utiliza para memorizar (cachear) funciones y así evitar que se creen nuevamente en cada renderizado, lo cual puede ayudar a mejorar el rendimiento de componentes, especialmente cuando estas funciones se pasan como props a componentes hijos que podrían renderearse innecesariamente.
Introducción a useCallback
-
Importación: Primero, necesitas importar el hook desde React.
import React, { useCallback } from 'react' -
Sintaxis básica:
const memoizedCallback = useCallback(() => {// Función lógica}, [dependencies])- Función lógica: La función que quieres memorizar.
- Dependencias: Una lista de variables que, si cambian, harán que la función se vuelva a crear.
Ejemplo Básico
Imaginemos que tenemos un componente que maneja un contador y pasa una función de incremento a un componente hijo.
Componente Padre
import React, { useState, useCallback } from 'react'import ChildComponent from './ChildComponent'
function ParentComponent() { const [count, setCount] = useState(0)
const increment = useCallback(() => { setCount((c) => c + 1) }, [])
return ( <div> <p>Count: {count}</p> <ChildComponent onIncrement={increment} /> </div> )}
export default ParentComponent
Componente Hijo
import React from 'react'
function ChildComponent({ onIncrement }) { console.log('ChildComponent rendered') return <button onClick={onIncrement}>Increment from Child</button>}
export default ChildComponent
Desglose del Ejemplo
-
Creación del Estado y Función de Incremento:
const [count, setCount] = useState(0)const increment = useCallback(() => {setCount((c) => c + 1)}, [])useState
se utiliza para manejar el estado del contador.useCallback
memoriza la funciónincrement
para evitar que se recree en cada renderizado, a menos que cambien sus dependencias (que en este caso es una lista vacía, por lo tanto nunca cambia). -
Uso de la Función en el Componente Hijo:
<ChildComponent onIncrement={increment} />La función
increment
se pasa al componente hijoChildComponent
como una prop. -
Renderizado Condicional del Componente Hijo:
console.log('ChildComponent rendered')El componente hijo solo se renderiza de nuevo si sus props cambian. Al usar
useCallback
, garantizamos que la funciónincrement
no cambia en cada renderizado del componente padre, evitando renderizados innecesarios del componente hijo.
Uso Avanzado
useCallback
es especialmente útil cuando se trabaja con listas de elementos o componentes complejos que podrían renderearse muchas veces.
Ejemplo con Lista de Items
Imaginemos que tenemos una lista de items y queremos manejar eventos en cada item de manera eficiente.
Componente Padre
import React, { useState, useCallback } from 'react'import ListItem from './ListItem'
function ItemList() { const [items, setItems] = useState(['Item 1', 'Item 2', 'Item 3'])
const handleItemClick = useCallback((item) => { console.log(`Clicked on: ${item}`) }, [])
return ( <ul> {items.map((item) => ( <ListItem key={item} item={item} onClick={handleItemClick} /> ))} </ul> )}
export default ItemList
Componente Hijo (ListItem)
import React from 'react'
function ListItem({ item, onClick }) { console.log(`Rendering: ${item}`) return <li onClick={() => onClick(item)}>{item}</li>}
export default ListItem
Desglose del Ejemplo de Lista
-
Función de Manejo de Click:
const handleItemClick = useCallback((item) => {console.log(`Clicked on: ${item}`)}, [])useCallback
memoriza la funciónhandleItemClick
, asegurando que no se recree en cada renderizado del componente padre. -
Renderizado de la Lista de Items:
{items.map((item) => <ListItem key={item} item={item} onClick={handleItemClick} />)}Cada
ListItem
recibe la misma instancia dehandleItemClick
, evitando renderizados innecesarios.
Comparación con useMemo
Mientras que useCallback
memoriza funciones, useMemo
memoriza el resultado de funciones. Ambos hooks son útiles para optimización, pero se utilizan en contextos ligeramente diferentes.
Ejemplo Comparativo
import React, { useState, useCallback, useMemo } from 'react'
function ParentComponent() { const [count, setCount] = useState(0)
const increment = useCallback(() => { setCount((c) => c + 1) }, [])
const memoizedValue = useMemo(() => { return count * 2 }, [count])
return ( <div> <p>Memoized Value: {memoizedValue}</p> <button onClick={increment}>Increment</button> </div> )}
export default ParentComponent
Desglose del Ejemplo Comparativo
-
useCallback
:const increment = useCallback(() => {setCount((c) => c + 1)}, [])Memoriza la función
increment
para que no cambie entre renderizados, a menos que cambien las dependencias. -
useMemo
:const memoizedValue = useMemo(() => {return count * 2}, [count])Memoriza el valor calculado
count * 2
y solo lo recalcula cuandocount
cambia.