# Plan — React Doctor Fixes ✅ COMPLETADO (2026-05-11)

**Score final: 52/100 → 78/100 (scan parcial archivos modificados: 78/100)**

## Contexto
`react-doctor` detectó 2296 issues (score 52/100). Este plan prioriza bugs reales. Se **ignoran** intencionalmente:
- Hydration mismatch (×151) — falso positivo, SPA sin SSR
- Giant components / useReducer — sugerencias arquitecturales
- Em dash / ellipsis — cosmético

---

## Fase 1 — Bugs críticos (correctness)

### 1.1 Rules of Hooks (×69) 🔴
Hooks llamados después de early returns → crashes en runtime.

**Archivo base:** `src/components/organismos/tablas/TablaMetodosPago.jsx:28`
— `if (data==null) return;` antes de `useState` calls

**Fix patrón:** mover todos los hooks antes de cualquier `return` condicional.

**Acción:** Grep `if (` antes de hooks en todos los archivos; ejecutar `npx eslint --rule 'react-hooks/rules-of-hooks: error'` para detectar todos los 69.

### 1.2 Nested component definitions (×7) 🔴
Componente dentro de otro → nueva instancia cada render → pierde estado.

**Archivos confirmados:**
- `src/components/organismos/POSDesign/SelectAlmacenModal.jsx:70` — `Controladorinsertarventas` dentro de `SelectAlmacenModal`
- `src/components/inventario/CategoriasTab.jsx:256` — `CategoriaItem` dentro de `CategoriasTab`

**Fix:** Mover al scope de módulo, pasar datos necesarios como props.

---

## Fase 2 — Memory leaks (Effect cleanup ×10) 🟠

**Archivos confirmados:**
- `src/hooks/useSupabaseSubscription.jsx:7` — falta `sub.unsubscribe()` antes de `removeChannel()`
- `src/utils/Efectonieve/NieveEffect.jsx:107` — falta `removeEventListener("resize", handleResize)` en cleanup
- `src/store/SyncStore.jsx:110` — listeners duplicados sin deduplication

**Fix patrón:**
```jsx
useEffect(() => {
  const sub = channel.subscribe(handler);
  return () => { sub.unsubscribe(); supabase.removeChannel(channel); };
}, []);
```

---

## Fase 3 — Performance

### 3.1 Functional setState (×134) — top hotspots 🟡
- `src/components/templates/POSAdminTemplate.jsx:700` — `setItems([...items, nuevoItem])`
- `src/components/ventas/NuevaRemisionTemplate.jsx:406,408`
- `src/components/inventario/InventarioFisicoTab.jsx:771` — `setSkip(skip + take)`

**Fix:** `setState(prev => ...)` en todos los casos donde el nuevo valor depende del anterior.

### 3.2 Default prop [] / {} (×43) 🟡
Nueva referencia en cada render → re-renders innecesarios.

**Top archivos:**
- `src/components/organismos/tablas/TablaInventarios.jsx:16`
- `src/components/templates/listas-precios/ListasPreciosTemplate.jsx:9`
- `src/components/importaciones/CostoFormDialog.jsx:69`
- +6 en tanstack stacks

**Fix:** `const EMPTY = []; function Comp({ data = EMPTY })`

### 3.3 filter().map() (×44) — solo arrays grandes 🟢
- `src/components/ventas/FacturasTab.jsx:103,738`
- `src/components/pedidos/CatalogPanel.jsx:376,438`

---

## Fase 4 — Accesibilidad (×11 errores alt text) 🟡

**Archivos:**
- `src/components/organismos/LandingPages/LandingPagesWelcome.jsx:41,53,64,141,152`
- `src/components/templates/Welcome.jsx:57,63,69,75,83,89`
- `src/components/organismos/LoginDesign/CardModos.jsx:16`
- `src/components/templates/RegisterTemplate.jsx:197`

**Fix:** `alt=""` para decorativas, texto descriptivo para informativas.

---

## Orden de ejecución

| Pri | Fase | Issues | Impacto |
|-----|------|--------|---------|
| 1 | Rules of Hooks | 69 | 🔴 Crashes |
| 2 | Nested components | 7 | 🔴 State loss |
| 3 | Effect cleanup | 10 | 🟠 Memory leaks |
| 4 | Functional setState | ~20 | 🟡 Stale bugs |
| 5 | Default prop refs | ~15 | 🟡 Re-renders |
| 6 | Alt text | 11 | 🟡 A11y errors |
| 7 | filter().map() | top 10 | 🟢 Minor perf |

## Verificación
```bash
npx eslint src --rule 'react-hooks/rules-of-hooks: error' --ext .jsx,.js
npx vite build
npx react-doctor@latest .  # comparar score vs 52/100
```
