TechLead

Optimizacion de Rendimiento

Memorizacion, code splitting y buenas practicas de React

Rendimiento en React

React es rapido por defecto, pero a medida que tu aplicacion crece, puedes necesitar optimizaciones. Aqui estan las tecnicas mas importantes.

React.memo

import { memo } from 'react';

// Componente que solo se re-renderiza si sus props cambian
const ItemLista = memo(function ItemLista({ titulo, onClick }) {
  console.log('Renderizando:', titulo);
  return <li onClick={onClick}>{titulo}</li>;
});

// Con comparacion personalizada
const ItemListaPersonalizado = memo(
  function ItemLista({ item }) {
    return <li>{item.nombre}</li>;
  },
  (prevProps, nextProps) => {
    // Retorna true si no debe re-renderizar
    return prevProps.item.id === nextProps.item.id;
  }
);

useMemo y useCallback

import { useMemo, useCallback, useState } from 'react';

function ListaFiltrada({ items }) {
  const [filtro, setFiltro] = useState('');
  const [seleccionado, setSeleccionado] = useState(null);

  // Memoriza el resultado del calculo costoso
  const itemsFiltrados = useMemo(() => {
    return items.filter(item =>
      item.nombre.toLowerCase().includes(filtro.toLowerCase())
    );
  }, [items, filtro]);

  // Memoriza la funcion para evitar re-renders de hijos
  const handleSeleccionar = useCallback((id) => {
    setSeleccionado(id);
  }, []);

  return (
    <div>
      <input
        value={filtro}
        onChange={(e) => setFiltro(e.target.value)}
      />
      <ul>
        {itemsFiltrados.map(item => (
          <ItemLista
            key={item.id}
            titulo={item.nombre}
            onClick={() => handleSeleccionar(item.id)}
          />
        ))}
      </ul>
    </div>
  );
}

Code Splitting con lazy

import { lazy, Suspense } from 'react';

// Importacion dinamica - el componente se carga solo cuando se necesita
const Dashboard = lazy(() => import('./Dashboard'));
const Configuracion = lazy(() => import('./Configuracion'));

function App() {
  return (
    <Suspense fallback={<p>Cargando...</p>}>
      <Routes>
        <Route path="/dashboard" element={<Dashboard />} />
        <Route path="/configuracion" element={<Configuracion />} />
      </Routes>
    </Suspense>
  );
}

Virtualizacion de Listas

// npm install @tanstack/react-virtual

import { useVirtualizer } from '@tanstack/react-virtual';

function ListaVirtual({ items }) {
  const contenedorRef = useRef(null);

  const virtualizador = useVirtualizer({
    count: items.length,
    getScrollElement: () => contenedorRef.current,
    estimateSize: () => 50, // altura estimada de cada item
  });

  return (
    <div ref={contenedorRef} style={{ height: '400px', overflow: 'auto' }}>
      <div style={{ height: virtualizador.getTotalSize() }}>
        {virtualizador.getVirtualItems().map((itemVirtual) => (
          <div
            key={itemVirtual.key}
            style={{
              position: 'absolute',
              top: itemVirtual.start,
              height: itemVirtual.size,
            }}
          >
            {items[itemVirtual.index].nombre}
          </div>
        ))}
      </div>
    </div>
  );
}

Evitar Errores Comunes

// MAL - Crea nuevo objeto en cada render
<Componente style={{ color: 'red' }} />

// BIEN - Objeto estable
const estilos = { color: 'red' };
<Componente style={estilos} />

// MAL - Crea nueva funcion en cada render
<Boton onClick={() => handleClick(id)} />

// BIEN - Con useCallback
const handleClickMemo = useCallback(() => handleClick(id), [id]);
<Boton onClick={handleClickMemo} />

// MAL - Estado innecesariamente en objeto
const [estado, setEstado] = useState({ nombre: '', email: '' });

// MEJOR - Estados separados si se actualizan independientemente
const [nombre, setNombre] = useState('');
const [email, setEmail] = useState('');

Reglas de Optimizacion

  • Mide primero: Usa React DevTools Profiler antes de optimizar
  • No optimices prematuramente: Muchas optimizaciones anaden complejidad
  • memo para listas: Muy util cuando renderizas muchos items
  • Divide componentes: Componentes mas pequenos = re-renders mas precisos