fix: corregir errores de React useLayoutEffect y code splitting

- Asegurar que React se carga antes que otros chunks en vite.config.ts
- Incluir scheduler en vendor-react para evitar errores de useLayoutEffect
- Suprimir errores de extensiones del navegador (message channel closed)
- Mejorar manejo de errores en main.tsx con mensajes amigables
This commit is contained in:
planetazuzu 2026-01-02 10:11:56 +01:00
parent 6df53a2c88
commit 53fad0c4b0
2 changed files with 55 additions and 13 deletions

View file

@ -2,6 +2,24 @@ import { createRoot } from "react-dom/client";
import App from "./App.tsx"; import App from "./App.tsx";
import "./index.css"; import "./index.css";
// Suprimir errores de extensiones del navegador (no críticos)
if (typeof window !== 'undefined') {
const originalError = console.error;
console.error = (...args: any[]) => {
// Filtrar errores de extensiones del navegador
const message = args[0]?.toString() || '';
if (
message.includes('message channel closed') ||
message.includes('runtime.lastError') ||
message.includes('Extension context invalidated')
) {
// Silenciar estos errores (son de extensiones del navegador, no de nuestra app)
return;
}
originalError.apply(console, args);
};
}
// CRÍTICO: Desregistrar Service Worker en desarrollo ANTES de cualquier otra cosa // CRÍTICO: Desregistrar Service Worker en desarrollo ANTES de cualquier otra cosa
// Esto evita que el SW intercepte peticiones de Vite HMR // Esto evita que el SW intercepte peticiones de Vite HMR
if ('serviceWorker' in navigator) { if ('serviceWorker' in navigator) {
@ -102,4 +120,26 @@ if ('serviceWorker' in navigator) {
} }
} }
createRoot(document.getElementById("root")!).render(<App />); // Asegurar que React está disponible antes de renderizar
const rootElement = document.getElementById("root");
if (!rootElement) {
throw new Error("Root element not found");
}
// Renderizar la app
// Nota: Si ves errores de "useLayoutEffect", puede ser un problema de code splitting.
// Asegúrate de que vendor-react se carga antes que otros chunks.
try {
createRoot(rootElement).render(<App />);
} catch (error) {
console.error('[React] Error rendering app:', error);
// Mostrar mensaje de error amigable
rootElement.innerHTML = `
<div style="padding: 2rem; text-align: center; font-family: sans-serif;">
<h1>Error al cargar la aplicación</h1>
<p>Por favor, recarga la página. Si el problema persiste, limpia la caché del navegador.</p>
<p style="color: #666; font-size: 0.9rem;">Error: ${error instanceof Error ? error.message : String(error)}</p>
</div>
`;
throw error;
}

View file

@ -50,10 +50,11 @@ export default defineConfig({
// Separar node_modules en chunks por librería // Separar node_modules en chunks por librería
if (id.includes('node_modules')) { if (id.includes('node_modules')) {
// React y React DOM juntos (crítico, cargar primero) // React y React DOM juntos (crítico, cargar primero)
if (id.includes('react') || id.includes('react-dom')) { // IMPORTANTE: Debe ser el primer chunk para evitar errores de useLayoutEffect
if (id.includes('react') || id.includes('react-dom') || id.includes('scheduler')) {
return 'vendor-react'; return 'vendor-react';
} }
// React Router (crítico para navegación) // React Router (crítico para navegación, depende de React)
if (id.includes('react-router')) { if (id.includes('react-router')) {
return 'vendor-router'; return 'vendor-router';
} }
@ -61,43 +62,44 @@ export default defineConfig({
if (id.includes('react-markdown') || id.includes('remark') || id.includes('rehype') || id.includes('unified') || id.includes('micromark') || id.includes('mdast')) { if (id.includes('react-markdown') || id.includes('remark') || id.includes('rehype') || id.includes('unified') || id.includes('micromark') || id.includes('mdast')) {
return 'vendor-markdown'; return 'vendor-markdown';
} }
// Radix UI (componentes UI, agrupar) // Radix UI (componentes UI, agrupar, depende de React)
if (id.includes('@radix-ui')) { if (id.includes('@radix-ui')) {
return 'vendor-ui'; return 'vendor-ui';
} }
// TanStack Query // TanStack Query (depende de React)
if (id.includes('@tanstack')) { if (id.includes('@tanstack')) {
return 'vendor-query'; return 'vendor-query';
} }
// Icons (lucide-react) // Icons (lucide-react, depende de React)
if (id.includes('lucide-react')) { if (id.includes('lucide-react')) {
return 'vendor-icons'; return 'vendor-icons';
} }
// Charts (recharts, si se usa) // Charts (recharts, si se usa, depende de React)
if (id.includes('recharts')) { if (id.includes('recharts')) {
return 'vendor-charts'; return 'vendor-charts';
} }
// Formularios (react-hook-form, zod) // Formularios (react-hook-form, zod, depende de React)
if (id.includes('react-hook-form') || id.includes('zod') || id.includes('@hookform')) { if (id.includes('react-hook-form') || id.includes('zod') || id.includes('@hookform')) {
return 'vendor-forms'; return 'vendor-forms';
} }
// Date/time (date-fns, react-day-picker) // Date/time (date-fns, react-day-picker, depende de React)
if (id.includes('date-fns') || id.includes('react-day-picker')) { if (id.includes('date-fns') || id.includes('react-day-picker')) {
return 'vendor-dates'; return 'vendor-dates';
} }
// Carousel (embla) // Carousel (embla, puede depender de React)
if (id.includes('embla')) { if (id.includes('embla')) {
return 'vendor-carousel'; return 'vendor-carousel';
} }
// Themes (next-themes) // Themes (next-themes, depende de React)
if (id.includes('next-themes')) { if (id.includes('next-themes')) {
return 'vendor-themes'; return 'vendor-themes';
} }
// Sonner (toasts) // Sonner (toasts, depende de React)
if (id.includes('sonner')) { if (id.includes('sonner')) {
return 'vendor-toasts'; return 'vendor-toasts';
} }
// Resto de node_modules pequeños // Resto de node_modules pequeños (NO incluir nada que dependa de React)
// Si algo aquí usa React, moverlo arriba
return 'vendor-other'; return 'vendor-other';
} }