Problema/Solución
30 problemas comunes de React 19 que enfrentan los desarrolladores, cada uno con una solución concisa. Ordenados por frecuencia de más a menos común.
1. forwardRef se siente redundante después de actualizar
Problema: Tienes docenas de componentes forwardRef que ahora se sienten como boilerplate.
Solución: Acepta ref como una prop regular.
interface InputProps extends React.ComponentPropsWithoutRef<"input"> {
ref?: React.Ref<HTMLInputElement>;
}
export function Input({ ref, ...props }: InputProps) {
return <input ref={ref} {...props} />;
}2. Importación de useFormState rota
Problema: useFormState de react-dom advierte o falla.
Solución: Importa useActionState de react — la misma firma más isPending.
import { useActionState } from "react";
const [state, action, isPending] = useActionState(submit, null);3. Errores de desincronización de hidratación más visibles
Problema: El código que funcionaba en React 18 ahora lanza errores de hidratación.
Solución: Mueve window, Date.now(), Math.random() dentro de useEffect o un hijo solo para cliente.
4. <title> y <meta> duplicándose
Problema: react-helmet + la elevación de metadatos nativa de React 19 produce etiquetas duplicadas.
Solución: Elimina react-helmet. Renderiza <title>/<meta> directamente en cualquier lugar — React 19 los eleva.
5. Los errores de Server Action se muestran como digest genérico
Problema: Los errores lanzados dentro de Server Actions aparecen como Server Error sin stack en producción.
Solución: Devuelve errores como valores, no excepciones.
"use server";
export async function save(_: State, fd: FormData) {
const title = fd.get("title");
if (!title) return { error: "Título requerido" };
return { ok: true };
}6. use(promise) causando suspense infinita
Problema: El componente se suspende para siempre después de agregar use().
Solución: La promesa debe ser estable entre renderizaciones — créala en un padre o memoízala.
const userPromise = useMemo(() => fetchUser(id), [id]);
const user = use(userPromise);7. Los componentes asincronos cuelgan el cliente
Problema: Marcar un Client Component como async lanza "async/await aún no es compatible".
Solución: Los componentes asincronos son solo en el servidor. Obtén datos en el servidor, pasa como prop, o usa use() en el cliente.
8. El valor de useOptimistic parpadea hacia atrás
Problema: La interfaz de usuario optimista se revierte antes de que el servidor responda.
Solución: Envuelve la mutación en startTransition o llámala desde un Server Action.
startTransition(async () => {
addOptimistic(item);
await saveItem(item);
});9. Boilerplate del proveedor de contexto
Problema: <MyContext.Provider value={...}> se siente verboso.
Solución: Renderiza el contexto mismo — React 19 acepta <MyContext value={...}>.
10. Limpieza de ref faltante
Problema: Sin hook de desmontaje cuando se desmonta un ref del DOM.
Solución: Devuelve una función de limpieza desde el callback del ref.
<div ref={(node) => {
const ob = new ResizeObserver(() => {});
if (node) ob.observe(node);
return () => ob.disconnect();
}} />11. El estado del formulario no se reinicia después de enviar
Problema: Los campos del formulario mantienen sus valores después de un Server Action exitoso.
Solución: Reinicia vía form.reset() en un requestFormReset dentro de la acción, o usa una prop key no controlada.
<form key={submitCount} action={action}>...</form>12. Choque del estado inicial de useActionState
Problema: Pasar undefined como estado inicial rompe el primer renderizado.
Solución: Siempre pasa un valor por defecto explícito como null u {}.
13. useFormStatus siempre devuelve pending: false
Problema: useFormStatus() llamado en el padre del <form> nunca se actualiza.
Solución: Solo lee el estado de un <form> ancestro. Llámalo desde un hijo renderizado dentro del formulario.
14. Server Action devolviendo datos no serializables
Problema: Devolver Date, Map, o instancias de clase silenciosamente rompe.
Solución: Devuelve solo objetos planos compatibles con JSON.
15. Metadatos dentro de Suspense no apareciendo en <head>
Problema: <title> se renderiza tarde cuando está dentro de un árbol suspendido.
Solución: Eleva metadatos por encima del límite de Suspense — los layouts son ideales.
16. Componentes genéricos de forwardRef pierden inferencia de TS
Problema: Convertir un forwardRef genérico a prop ref pierde la inferencia de tipos.
Solución: Mantén forwardRef para ese componente, o ajusta la restricción genérica.
17. useTransition no aplazando renderizaciones costosas
Problema: La interfaz de usuario sigue paradeando incluso después de envolver en startTransition.
Solución: Asegúrate de que el trabajo costoso lee del estado actualizado dentro de la transición, no un setState síncrono separado.
18. Server Component importando una librería solo para cliente cuelga
Problema: Importar una librería solo para navegador en un Server Component lanza window is not defined.
Solución: Agrega "use client" en la parte superior del archivo, o importa dinámicamente con next/dynamic + ssr: false.
19. cache() no deduplicando entre solicitudes
Problema: cache() de React parece cachear por solicitud pero no entre solicitudes.
Solución: Eso es por diseño — cache() es memoización por solicitud. Para entre solicitudes, usa unstable_cache (Next.js) o un caché externo.
20. use() en un condicional después de hooks da error
Problema: Llamar use() dentro de un if después de otro hook rompe las reglas de hooks.
Solución: use() puede ser condicional, pero sigue contando como una llamada a hook — mantenlo antes de cualquier retorno anticipado.
21. Strict Mode invoca doble Server Actions en desarrollo
Problema: Server Action se ejecuta dos veces en desarrollo.
Solución: Esa es la invocación doble intencional de Strict Mode. Haz las acciones idempotentes o deshabilita Strict Mode solo como último recurso.
22. ref como prop + librería de terceros esperando forwardRef
Problema: Una librería envuelve tu componente y espera semántica de forwardRef.
Solución: Mantén forwardRef en el límite de integración — la compatibilidad sigue funcionando.
23. Parpadeo del valor inicial de useDeferredValue
Problema: En el primer renderizado quieres omitir el valor antiguo.
Solución: React 19 acepta un segundo argumento: useDeferredValue(value, initialValue).
const deferred = useDeferredValue(query, "");24. Parpadeo del fallback de Suspense en navegación
Problema: El fallback parpadea incluso para transiciones rápidas.
Solución: Envuelve la navegación en startTransition para que React muestre contenido antiguo en lugar del fallback.
25. <link rel="stylesheet"> cargando fuera de orden
Problema: Múltiples componentes renderizadores de la misma hoja de estilo y el orden es inestable.
Solución: Usa la prop precedence — React 19 deduplica y las ordena.
<link rel="stylesheet" href="/a.css" precedence="default" />26. Precarga manual de recursos
Problema: Inyectas <link rel="preload"> a mano y se ejecuta muy tarde.
Solución: Usa las APIs de recursos de React 19: preload, preinit, prefetchDNS de react-dom.
import { preload } from "react-dom";
preload("/hero.jpg", { as: "image" });27. Error boundary no capturando errores de Server Action
Problema: El error de Server Action pasa por alto tu <ErrorBoundary>.
Solución: Los límites de error capturan errores de renderizado, no rechazo de acciones. Expón errores vía valor de retorno de useActionState en su lugar.
28. Error de TypeScript en formulario action={serverAction}
Problema: TS se queja de pasar una función del servidor a action.
Solución: Asegúrate de @types/react ≥ 19 y que la firma de acción coincida con (formData: FormData) => void | Promise<void>.
29. Archivo "use server" filtrando código del cliente
Problema: Importar utilidades compartidas en un archivo "use server" agrupa código solo para cliente del lado del servidor.
Solución: Mantén los archivos "use server" enfocados en acciones — extrae lógica compartida a un archivo neutral lib/ sin importaciones de React.
30. Migración rompe ReactDOM.render heredado
Problema: El código de bootstrap antiguo usando ReactDOM.render ya no compila.
Solución: Cambia a createRoot (requerido desde 18, aplicado en 19).
import { createRoot } from "react-dom/client";
createRoot(document.getElementById("root")!).render(<App />);Relacionado
- React Rules — Prácticas recomendadas de React
- Next.js Rules — Orientación específica de App Router
- API Rules — Patrones de Server Action y API