# React Optimizacion — Patrones avanzados y ejemplos completos

## Concurrent Features

### useTransition — marcar actualizaciones como no urgentes

```tsx
import { useTransition, useState } from 'react';

function BuscadorFacturas() {
  const [query, setQuery] = useState('');
  const [resultados, setResultados] = useState<Factura[]>([]);
  const [isPending, startTransition] = useTransition();

  const handleBusqueda = (valor: string) => {
    // Actualización urgente: el input responde inmediatamente
    setQuery(valor);

    // Actualización no urgente: los resultados pueden esperar
    startTransition(() => {
      const filtrados = buscarFacturas(valor);
      setResultados(filtrados);
    });
  };

  return (
    <div>
      <input
        value={query}
        onChange={e => handleBusqueda(e.target.value)}
        placeholder="Buscar facturas..."
      />
      {isPending && <span className="text-gray-400">Buscando...</span>}
      <ListaResultados resultados={resultados} />
    </div>
  );
}
```

### useDeferredValue — versión diferida de un valor

```tsx
import { useDeferredValue, memo } from 'react';

function ListaFiltrada({ items }: { items: string[] }) {
  const [filtro, setFiltro] = useState('');
  // Versión diferida del filtro — permite que el input sea fluido
  const filtroDeferred = useDeferredValue(filtro);

  // isStale detecta si estamos mostrando resultado desactualizado
  const isStale = filtro !== filtroDeferred;

  const itemsFiltrados = useMemo(
    () => items.filter(item => item.includes(filtroDeferred)),
    [items, filtroDeferred]
  );

  return (
    <div>
      <input value={filtro} onChange={e => setFiltro(e.target.value)} />
      <ul style={{ opacity: isStale ? 0.6 : 1 }}>
        {itemsFiltrados.map(item => <li key={item}>{item}</li>)}
      </ul>
    </div>
  );
}
```

---

## Virtualización para listas largas

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

function ListaVirtualizada({ facturas }: { facturas: Factura[] }) {
  const contenedorRef = useRef<HTMLDivElement>(null);

  const virtualizer = useVirtualizer({
    count: facturas.length,
    getScrollElement: () => contenedorRef.current,
    estimateSize: () => 80,  // altura estimada en px por fila
    overscan: 5,             // filas extra fuera del viewport
  });

  return (
    <div ref={contenedorRef} style={{ height: '600px', overflow: 'auto' }}>
      <div style={{ height: `${virtualizer.getTotalSize()}px`, position: 'relative' }}>
        {virtualizer.getVirtualItems().map(virtualItem => (
          <div
            key={virtualItem.key}
            style={{
              position: 'absolute',
              top: 0,
              left: 0,
              width: '100%',
              transform: `translateY(${virtualItem.start}px)`,
            }}
          >
            <FacturaCard factura={facturas[virtualItem.index]} />
          </div>
        ))}
      </div>
    </div>
  );
}
```

---

## Optimización de Context — ejemplo completo

El contexto causa re-render en TODOS los consumidores cuando cambia.
Dividir en contextos más pequeños o usar selectores:

```tsx
// MAL: un solo contexto con todo el estado
const AppContext = createContext<AppState>({...});

// BIEN: separar por dominio y frecuencia de cambio
const AuthContext = createContext<AuthState>({...});      // Cambia raramente
const UIContext = createContext<UIState>({...});          // Cambia frecuentemente
const FacturasContext = createContext<FacturasState>({...}); // Cambia al interactuar

// BIEN: con useMemo para estabilizar el valor del contexto
function AuthProvider({ children }: { children: React.ReactNode }) {
  const [usuario, setUsuario] = useState<Usuario | null>(null);

  const value = useMemo(() => ({
    usuario,
    login: async (creds: Credentials) => { ... },
    logout: () => setUsuario(null),
  }), [usuario]);  // Solo se crea objeto nuevo cuando usuario cambia

  return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
```
