Busca en todas las páginas de la documentación
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥
# Versión de módulo ES que puede hacer tree-shaking (recomendado)
npm install lodash-es
npm install -D @types/lodash-es
# O versión clásica con importaciones seleccionadas
npm install lodash
npm install -D @types/lodash// Importa funciones individuales para tree-shaking
import debounce from "lodash-es/debounce";
import groupBy from "lodash-es/groupBy";
import cloneDeep from "lodash-es/cloneDeep";
// O importaciones nombradas (funciona con lodash-es)
import { debounce, groupBy, cloneDeep } from "lodash-es";Cuándo usarlo: Necesitas funciones utilitarias confiables para debouncing, throttling, clonación profunda, agrupación o fusión que manejen casos extremos mejor que soluciones rápidas caseras.
// app/components/SearchWithDebounce.tsx
"use client";
import { useState, useCallback, useMemo, useEffect } from "react";
import debounce from "lodash-es/debounce";
import groupBy from "lodash-es/groupBy";
interface Product {
id: number;
name: string;
category: string;
price: number;
}
const ALL_PRODUCTS: Product[] = [
{ id: 1, name: "React Handbook", category: "books", price: 29 },
{ id: 2, name: "TypeScript Guide", category: "books", price: 35 },
{ id: 3, name: "Mechanical Keyboard", category: "electronics", price: 150 },
{ id: 4, name: "USB-C Hub", category: "electronics", price: 45 },
{ id: 5, name: "Standing Desk", category: "furniture", price: 400 },
{ id: 6, name: "Monitor Arm", category: "furniture", price: 80 },
];
export default function SearchWithDebounce() {
const [query, setQuery] = useState("");
const [results, setResults] = useState<Product[]>(ALL_PRODUCTS);
const searchProducts = useMemo(
() =>
debounce((searchQuery: string) => {
const filtered = ALL_PRODUCTS.filter((p) =>
p.name.toLowerCase().includes(searchQuery.toLowerCase())
);
setResults(filtered);
}, 300),
[]
);
// Limpieza de debounce al desmontar
useEffect(() => {
return () => {
searchProducts.cancel();
};
}, [searchProducts]);
const handleChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
setQuery(e.target.value);
searchProducts(e.target.value);
},
[searchProducts]
);
const grouped = groupBy(results, "category");
return (
<div className="max-w-lg mx-auto p-6">
<input
value={query}
onChange={handleChange}
placeholder="Buscar productos..."
className="w-full border rounded px-3 py-2 mb-4"
/>
{Object.entries(grouped).map(([category, items]) => (
<div key={category} className="mb-4">
<h3 className="font-bold capitalize text-lg">{category}</h3>
<ul className="mt-1 space-y-1">
{items.map((item) => (
<li key={item.id} className="flex justify-between">
<span>{item.name}</span>
<span className="text-gray-500">${item.price}</span>
</li>
))}
</ul>
</div>
))}
<p className="text-sm text-gray-400 mt-4">
{results.length} resultados encontrados
</p>
</div>
);
}Lo que demuestra esto:
groupBy para organizar resultados por categoríauseMemo para crear una referencia estable a la función debouncedlodash-es proporciona exportaciones de módulos ES, permitiendo que los bundlers (webpack, Rollup, esbuild) hagan tree-shaking de funciones no utilizadasdebounce retrasa la ejecución de funciones hasta que ha transcurrido un tiempo de espera específico desde la última llamada; cancel() previene invocaciones pendientesthrottle limita la ejecución a como máximo una vez por intervalo, útil para manejadores de scroll/resizegroupBy crea un objeto donde las claves provienen del iteratee y los valores son arrays de elementos coincidentescloneDeep copia recursivamente objetos, incluyendo objetos anidados, arrays, Maps, Sets e instancias de Datemerge fusiona objetos profundamente, mientras que Object.assign y spread solo hacen fusión superficialThrottle para manejadores de scroll/resize:
"use client";
import { useEffect, useState } from "react";
import throttle from "lodash-es/throttle";
export function useScrollPosition() {
const [scrollY, setScrollY] = useState(0);
useEffect(() => {
const handleScroll = throttle(() => {
setScrollY(window.scrollY);
}, 100);
window.addEventListener("scroll", handleScroll);
return () => {
handleScroll.cancel();
window.removeEventListener("scroll", handleScroll);
};
}, []);
return scrollY;
}Fusión profunda de objetos de configuración:
import merge from "lodash-es/merge";
const defaultConfig = {
theme: { colors: { primary: "#3b82f6", secondary: "#64748b" } },
features: { darkMode: false, notifications: true },
};
const userConfig = {
theme: { colors: { primary: "#ef4444" } },
features: { darkMode: true },
};
const config = merge({}, defaultConfig, userConfig);
// { theme: { colors: { primary: "#ef4444", secondary: "#64748b" } },
// features: { darkMode: true, notifications: true } }Clonación profunda segura:
import cloneDeep from "lodash-es/cloneDeep";
const original = {
nested: { value: 42, date: new Date(), set: new Set([1, 2, 3]) },
};
const copy = cloneDeep(original);
copy.nested.value = 100;
console.log(original.nested.value); // 42 (sin cambios)Otras utilidades comunes:
import pick from "lodash-es/pick";
import omit from "lodash-es/omit";
import uniqBy from "lodash-es/uniqBy";
import chunk from "lodash-es/chunk";
import get from "lodash-es/get";
// Selecciona claves específicas de un objeto
const user = { id: 1, name: "Alice", email: "alice@example.com", password: "secret" };
const safe = pick(user, ["id", "name", "email"]);
// Elimina claves
const noPassword = omit(user, ["password"]);
// Deduplica por una propiedad
const unique = uniqBy(users, "email");
// Divide array en páginas
const pages = chunk(items, 10); // [[...10], [...10], ...]
// Acceso seguro a propiedades profundas (considera optional chaining en su lugar)
const value = get(config, "deeply.nested.value", "default");@types/lodash-es proporciona definiciones de tipos completas para todas las funcionesdebounce y throttle devuelven DebouncedFunc<T> con métodos cancel() y flush()groupBy devuelve Dictionary<T[]> donde las claves son stringsimport type { DebouncedFunc } from "lodash-es";
// Función debounced explícitamente tipada
const debouncedSearch: DebouncedFunc<(query: string) => void> = debounce(
(query: string) => {
console.log("Buscando:", query);
},
300
);Importar todo de lodash - import _ from "lodash" agrupa toda la librería (alrededor de 70KB minificado). Solución: Usa lodash-es con importaciones nombradas o importa rutas específicas como lodash-es/debounce.
Debounce en render - Crear una función debounced dentro de render crea una nueva instancia cada render, frustrando el propósito. Solución: Envuelve con useMemo o useCallback y proporciona una referencia estable.
Memory leaks de debounce/throttle - Las llamadas debounced pendientes pueden ejecutarse después del desmontaje del componente. Solución: Llama .cancel() en la función de limpieza de useEffect.
cloneDeep es costoso - Clonar profundamente objetos grandes es lento. Solución: Usa structuredClone() (nativo, disponible en todos los navegadores modernos y Node 17+) para casos simples. Usa cloneDeep solo cuando necesites manejar funciones o características especiales de Lodash.
get vs optional chaining - _.get(obj, "a.b.c") es redundante ahora que JavaScript tiene obj?.a?.b?.c. Solución: Prefiere optional chaining para acceso a propiedades. Usa get solo cuando la ruta es dinámica (una variable).
merge mutates el objetivo - merge(target, source) muta target. Solución: Pasa un objeto vacío como primer argumento: merge({}, defaults, overrides).
| Función | Alternativa Nativa | Cuándo Usar Lodash |
|---|---|---|
cloneDeep | structuredClone() | Cuando clonas funciones o instancias de clase |
get | Optional chaining (?.) | Cuando la ruta es una variable de cadena dinámica |
debounce | Sin equivalente nativo | Siempre (o usa un pequeño paquete use-debounce) |
throttle | Sin equivalente nativo | Siempre |
groupBy | Object.groupBy() (ES2024) | Cuando te diriges a entornos antiguos |
merge | Spread {...a, ...b} | Cuando necesitas fusión profunda (spread es superficial) |
uniqBy | [...new Map(arr.map(x => [x.key, x])).values()] | Cuando la legibilidad importa |
chunk | Sin equivalente nativo | Siempre |
lodash-es proporciona exportaciones de módulos ES que los bundlers pueden hacer tree-shakeimport { debounce } from "lodash" agrupa toda la librería (~70KB)import { debounce } from "lodash-es" incluye solo debounce y sus dependencias@types/lodash-es junto a lodash para soporte de TypeScriptconst searchFn = useMemo(
() => debounce((query: string) => {
// realizar búsqueda
}, 300),
[]
);
useEffect(() => {
return () => searchFn.cancel();
}, [searchFn]);Envuelve en useMemo para una referencia estable. Limpia con .cancel() al desmontar.
useMemo o useCallback para que la misma función debounced persista entre rendersdebounce espera N ms de inactividad antes de ejecutar (bueno para búsquedas de entrada)throttle se ejecuta como máximo una vez por intervalo de N ms (bueno para manejadores de scroll/resize).cancel() y .flush()merge mutates el primer argumento (el objetivo)merge({}, defaults, overrides)defaults y overrides sin cambiosstructuredClone() es nativo y maneja la mayoría de tipos (objetos, arrays, Maps, Sets, Dates)cloneDeep también maneja funciones, RegExp con banderas y envolturas específicas de lodashstructuredClone() para casos simples -- no se necesita dependenciacloneDeep cuando clonas objetos que contienen funciones o instancias de claseobj?.a?.b?.c -- es nativo y type-safeget sigue siendo útil cuando la ruta es una variable dinámica: get(obj, dynamicPath, defaultValue)get también soporta notación de array en rutas: get(obj, "items[0].name")import type { DebouncedFunc } from "lodash-es";
const debouncedSearch: DebouncedFunc<(q: string) => void> =
debounce((q: string) => {
console.log("Buscando:", q);
}, 300);DebouncedFunc<T> agrega .cancel() y .flush() al tipo de función envuelta.
import groupBy from "lodash-es/groupBy";
const products = [
{ name: "Book", category: "media" },
{ name: "CD", category: "media" },
{ name: "Desk", category: "furniture" },
];
const grouped = groupBy(products, "category");
// { media: [...], furniture: [...] }Devuelve Dictionary<T[]> donde las claves son strings.
import omit from "lodash-es/omit";
const user = { id: 1, name: "Alice", password: "secret" };
const safe = omit(user, ["password"]);
// { id: 1, name: "Alice" }omit devuelve un nuevo objeto; el original no se modifica.
import chunk from "lodash-es/chunk";
const items = [1, 2, 3, 4, 5, 6, 7];
const pages = chunk(items, 3);
// [[1, 2, 3], [4, 5, 6], [7]]Útil para paginación o procesamiento por lotes de solicitudes API.
.cancel() en la función de limpieza de useEffectdebounce como a throttleRevisado por Chris St. John·Última actualización: 16 jul 2026
🤖 Read the SystemsArchitect.io Blog for over 100+ cloud architecture articles 🔥