LifeHooks
1.2.2
Inicio

use-callback-ref

Mantiene la misma referencia de función entre renders, pero siempre ejecuta la versión más reciente. Perfecto para optimizar componentes y listeners.

¿Qué hace?

Devuelve una función que nunca cambia de referencia entre renders, pero siempre ejecuta la versión más reciente de tu callback.

Problema que resuelve:

  • Sin useCallbackRef: La función cambia en cada render → causa re-renders innecesarios
  • Con useCallbackRef: La función mantiene la misma referencia → evita re-renders

Importación

import { useCallbackRef } from "@blifedesarrollo/hooks";

Uso básico

Con listeners de eventos

function SearchInput({ onSearch }) {
  // onSearch puede cambiar en cada render del padre
  const stableOnSearch = useCallbackRef(onSearch);
 
  useEffect(() => {
    const handler = (e) => stableOnSearch(e.target.value);
    const input = document.getElementById("search");
 
    input.addEventListener("input", handler);
    return () => input.removeEventListener("input", handler);
    // ✅ stableOnSearch nunca cambia, no re-suscribe el evento
  }, [stableOnSearch]);
 
  return <input id="search" placeholder="Buscar..." />;
}

¿Por qué es útil?

  • onSearch puede cambiar en cada render del componente padre
  • stableOnSearch mantiene la misma referencia → el useEffect no se re-ejecuta
  • Pero siempre ejecuta la última versión de onSearch

Con componentes optimizados (memo)

import { useState, memo } from "react";
import { useCallbackRef } from "@blifedesarrollo/hooks";
 
// Componente hijo optimizado con memo
const TaskItem = memo(({ task, onToggle, onDelete }) => {
  return (
    <div>
      <input type="checkbox" checked={task.completed} onChange={() => onToggle(task.id)} />
      <span>{task.title}</span>
      <button onClick={() => onDelete(task.id)}>Eliminar</button>
    </div>
  );
});
 
function TodoApp() {
  const [tasks, setTasks] = useState([
    { id: 1, title: "Comprar leche", completed: false },
    { id: 2, title: "Revisar emails", completed: true },
  ]);
 
  // ✅ Referencia estable - TaskItem no se re-renderiza innecesariamente
  const handleToggleTask = useCallbackRef((taskId) => {
    setTasks((prevTasks) =>
      prevTasks.map((task) =>
        task.id === taskId ? { ...task, completed: !task.completed } : task,
      ),
    );
  });
 
  const handleDeleteTask = useCallbackRef((taskId) => {
    setTasks((prevTasks) => prevTasks.filter((t) => t.id !== taskId));
  });
 
  return (
    <div>
      {tasks.map((task) => (
        <TaskItem
          key={task.id}
          task={task}
          onToggle={handleToggleTask} // ← Misma referencia siempre
          onDelete={handleDeleteTask} // ← Misma referencia siempre
        />
      ))}
    </div>
  );
}

Ventaja: TaskItem con memo no se re-renderiza cuando cambia el estado, porque las funciones mantienen la misma referencia.

Acceder al estado actual sin dependencias

function Counter() {
  const [count, setCount] = useState(0);
 
  // ✅ No necesitas incluir 'count' en dependencias
  const handleClick = useCallbackRef(() => {
    setCount(count + 1);
    // Siempre accede al valor más reciente de 'count'
    console.log(`Contador: ${count + 1}`);
  });
 
  useEffect(() => {
    // handleClick nunca cambia, este efecto solo se ejecuta una vez
    window.addEventListener("click", handleClick);
    return () => window.removeEventListener("click", handleClick);
  }, [handleClick]);
 
  return <div>Count: {count}</div>;
}

Comparación con useCallback

CaracterísticauseCallbackuseCallbackRef
DependenciasRequiere array de depsNo requiere dependencias
Referencia estableSolo si deps no cambianSiempre estable
Versión ejecutadaVersión con deps actualesSiempre la más reciente
Caso de usoOptimización generalListeners, memo, refs

API

Parámetros

ParámetroTipoDescripción
callbackT | undefinedFunción que quieres mantener referenciada

Retorno

ValorTipoDescripción
stableCallbackTFunción con la misma firma, pero referencia estable entre renders

💡 Casos de uso comunes

  • Listeners de eventos → Evita re-suscribir eventos en cada render
  • Componentes con memo → Evita re-renders innecesarios de hijos
  • Refs y callbacks → Mantiene referencia estable para APIs externas
  • Sin dependencias → Accede al estado más reciente sin listar dependencias