Mejores Prácticas de Patrones React
Un resumen condensado de las 25 mejores prácticas más importantes extraídas de todas las páginas de esta sección.
- Prefiere Slots Nombrados en Lugar de children Único: Sobrecargar
childrenpara diseños multi-región pierde el control de colocación; expone props nombrados deReactNode—({ header, sidebar, footer }: { header: ReactNode; sidebar: ReactNode; footer: ReactNode })— para que los consumidores puedan llenar cada slot independientemente y mantengas TypeScript de su lado. - Elige ReactNode, ReactElement o ComponentType:
ReactNodeacepta strings/null/arrays (mejor para slots y children),ReactElementse reduce a un único elemento JSX, yComponentType<P>es un componente que puedes instanciar — elige intencionadamente porque escriben estilos de composición diferentes. - Construye Componentes Compuestos en Context: Una raíz compuesta es propietaria del estado y lo publica a través de context para que
Root.Item,Root.TriggeryRoot.Contentfuncionen a cualquier profundidad JSX — protege el hook del consumidor con un error lanzado cuando se usa fuera de la raíz en lugar de devolver unnullsilencioso. - Marca Raíces Compuestas como "use client": Los componentes compuestos dependen de
createContextyuseState, por lo que el archivo raíz debe ser un Client Component en Next.js — y usa un context de dos niveles (AccordionContext+ItemContextpor elemento) para limitar la identidad a cada elemento. - Divide Contextos de Estado y Acciones: Pon el estado en un context y acciones memoizadas en otro para que los botones que solo despachan nunca se re-renderizen en cambios de estado; el
dispatchdeuseReducerya es estable en React y no necesita memoización adicional. - Nunca Inlines Valores de Context de Objeto: Pasar un literal de objeto fresco como el
valuedel context en cada renderizado padre anula la memoización y re-renderiza cada consumidor; envuelve el valor enuseMemocon clave en el estado real, o divide contexts para que los consumidores solo actualizadores sean inmunes. - Alcance Proveedores Estrechamente: Monta un proveedor en el subtree más pequeño que realmente lo necesita; un proveedor en la raíz dispersa re-renderizados en todas las páginas no relacionadas y hace que la división de código perezoso sea menos efectiva.
- Soporta Tanto Controlado como No Controlado: Los componentes de biblioteca deben detectar el modo controlado a través de
value !== undefinedy retroceder al estado interno —const [inner, setInner] = useState(defaultValue); const val = value !== undefined ? value : inner— envuelve la decisión en un hook reutilizableuseControllableStatey nunca cambies modos después del montaje. - No Desaceleres Actualizaciones de Estado para Inputs Controlados: Desacelerar el
onChangeque establece estado congela la entrada porque el valor del DOM se queda atrás del usuario; desacelera efectos secundarios (búsqueda, autosave) mientras siempre actualizas el estado controlado sincronamente. - Los Límites de Error Necesitan getDerivedStateFromError: Implementa
getDerivedStateFromError(derivación de estado de fase de renderizado puro) para el cambio de fallback ecomponentDidCatch(post-commit) para registrar en Sentry; no hay equivalente de hook, así que usa una clase oreact-error-boundary. - Restablece Límites a través de resetKeys + key: Usa
resetKeyspara que el límite se recupere automáticamente cuando cambie un parámetro de ruta, y pon unakeyen los hijos del límite para que limpiarhasErrorfuerce un remontaje — simplemente restablecer el estado deja el subtree en su estado corrupto. - Sabe Qué Pierden los Límites de Error: No capturan errores en manejadores de eventos, código asincrónico, SSR o el límite mismo; envuelve esos en
try/catchexplícito, y en Next.js confía en la convención de nivel de rutaerror.tsxconerror.digestpara errores del servidor. - Llama HOCs en Alcance de Módulo: Llamar a
withAuth(Component)dentro del cuerpo de otro componente crea un tipo de componente completamente nuevo en cada renderizado y destruye estado; aplica HOCs una vez en el alcance del módulo —const ProtectedPage = withAuth(DashboardPage)— y siempre establece undisplayNamesignificativo. - Reenvía Refs y Estáticos a través de HOCs: Los HOCs descartan refs y métodos estáticos por defecto, así que envuelve con
React.forwardRefinternamente y copia estáticos conhoist-non-react-statics— de lo contrario, los consumidores pierden mangos imperativos y constantes adjuntas. - Perfila Antes de que Memoices:
memo/useMemo/useCallbackprematuro agrega sobrecarga de comparación que puede ralentizar componentes rápidos; usa el Profiler de React DevTools para identificar puntos calientes reales y recuerda queonClick={() => …}ostyle={{…}}en línea anula memo mediante referencias frescas. - useTransition es Dueño, useDeferredValue Recibe: Usa
useTransitioncuando eres dueño del establecedor de estado y quieres marcar la actualización como no urgente; usauseDeferredValuepara retrasar un valor que recibes como prop, y compáralo con el valor actual para mostrar un indicador de obsoleto sutil. - Virtualiza Listas Grandes: Decenas de miles de filas no pueden ser rescatadas por
memo— llega areact-virtual/react-windowo CSScontent-visibility: auto, y manténkeys estables (IDs, no índices) para que las filas no se remunten al desplazarse. - createPortal Mantiene el Árbol React: Los eventos se propagan a través del árbol React (no del árbol DOM) a través de portales, así que los clics dentro de un modal aún golpean ancestros React — llama a
stopPropagationcuando necesites un corte limpio, y atrapa el enfoque además de guardarlo/restaurarlo para modales reales. - Protege Portales para SSR:
document.bodybloquea el renderizado del servidor, así que cierra el portal con una banderamountedestablecida poruseEffect—const [mounted, setMounted] = useState(false); useEffect(() => setMounted(true), [])— o marca el componente como"use client"en Next.js, y crea un nodo de contenedor dedicado en lugar de amontonar divs vacíos enbody. - Ten Cuidado con Render Props en Línea: Un
children={(state) => <X />}en línea crea una referencia de función nueva en cada renderizado e interrumpeReact.memoen el padre; eleva la función o acepta que memo no se aplicará aquí, y recuerda que los hooks no pueden ser llamados dentro de una función render-prop. - Exporta Colecciones de Props: Los componentes sin cabeza deben devolver objetos de props dispersables que agrupen manejadores de eventos más atributos ARIA —
<button {...triggerProps}>Open</button> <div {...contentProps}>…</div>— los consumidores no pueden olvidar el cableado de accesibilidad porque se envía con la colección. - Modela Estado como Uniones Discriminadas: Reemplaza la sopa booleana
isLoading/isError/hasDatacon un únicostatusde unión etiquetada para que combinaciones imposibles sean irreproducibles; cada estado lleva solo los datos válidos para ese estado. - Mantén Reductores Puros: Bajo Strict Mode y renderizado concurrente, React puede llamar a un reductor múltiples veces, así que nunca busques, escribas o registres dentro de él — y siempre termina con
return statepara eventos desconocidos para que transiciones no manejadas no produzcanundefined. - Crea Promesas de Suspense Fuera del Renderizado: Crear una promesa en el cuerpo del componente dispara un bucle de suspensión infinito porque
use(promise)trata una nueva referencia como una nueva solicitud pendiente; eleva la creación de promesas a un padre, un caché o un manejador de eventos. - Empareja Suspense con Límite de Error y Transición: Una promesa rechazada sin un límite de error ancestro desmonta el árbol completo; también usa
useTransitioncuando reemplazas la interfaz de usuario impulsada por Suspense para que las búsquedas rápidas no destelleen un esqueleto, y anida límites para que el contenido secundario se transmita después del shell crítico.