Inicio / Artículos

Diez problemas ocultos de los componentes React que ralentizan las aplicaciones en la era de React 19

Diez errores comunes en los componentes de React, desde HTML semántico faltante hasta la omisión de la memorización, por qué React 19 y el React Compiler no los ocultan, junto con soluciones.

Editor: REACTAPP.TOP16 min de lectura3642 palabras

Fuentes: 10 React Component Mistakes That Are Secretly Breaking Your App in 2026

Los componentes de React parecen sencillos hasta que pequeños errores se acumulan y generan problemas de accesibilidad, rendimiento lento y errores difíciles de detectar. React 19, el React Compiler y los Server Components han cambiado la forma en que se construyen las aplicaciones, lo que hace que estos errores tengan consecuencias aún más graves, no menos. A continuación se presentan diez errores a nivel de componentes que dañan silenciosamente una aplicación, junto con la solución para cada uno.

1. Usar <div> en todas partes en lugar de HTML semántico

El error: Recurrir a un genérico <div> para cada elemento de la interfaz de usuario. La navegación se convierte en un <div>. Un botón se transforma en <div onClick={...}>. Un artículo queda envuelto en un <div>. Incluso la etiqueta de un formulario recibe el mismo tratamiento.

// ❌ Bad: Div soup
function Navbar() {
  return (
    <div className="navbar">
      <div className="logo" onClick={() => router.push('/')}>Logo</div>
      <div className="nav-links">
        <div className="nav-item" onClick={() => setPage('home')}>Home</div>
        <div className="nav-item" onClick={() => setPage('about')}>About</div>
      </div>
    </div>
  );
}

Por qué daña su aplicación:

  • Problemas de accesibilidad: las tecnologías de asistencia no pueden reconocer los puntos de referencia de navegación, los controles interactivos ni los títulos. Quien use un lector de pantalla escuchará algo como “se puede hacer clic, texto: Inicio” en lugar de “navegación, enlace, Inicio”.
  • Acceso mediante teclado defectuoso: las etiquetas <div> no son enfocables por defecto, por lo que quienes naveguen con teclado no pueden acceder a su menú de navegación con la tecla Tab.
  • SEO más débil: los motores de búsqueda dependen de la marcación semántica como <nav>, <main> y <article> para analizar la estructura de una página. Una página construida íntegramente con <div> se considera contenido de baja calidad para los rastreadores.
  • Componentes de servidor en React 19: dado que el SSR y la transmisión en tiempo real hacen que el payload HTML inicial sea lo que ven tanto los usuarios como los bots antes de que se active la hidratación, la estructura semántica cobra aún más importancia.

La solución:

// ✅ Good: Semantic HTML
function Navbar() {
  return (
    <nav className="navbar" aria-label="Main navigation">
      <a href="/" className="logo">Logo</a>
      <ul className="nav-links">
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  );
}

Mejor práctica para 2026: Aproveche la nueva API use en React 19 junto con los metadatos de accesibilidad, y ejecute sus componentes mediante herramientas de auditoría como axe-core o la extensión del navegador WAVE. Utilice <button> para cualquier elemento clickeable, <nav> para bloques de navegación, y <section> junto con aria-labelledby al delimitar una región de la página.

2. Estilos en línea en JSX (que afectan el rendimiento)

El error: Definir objetos de estilo en línea dentro de JSX, lo cual genera una nueva referencia de objeto en cada renderizado.

// ❌ Bad: New object every render
function Badge({ color, children }) {
  return (
    <span style={{ backgroundColor: color, padding: '4px 8px', borderRadius: '4px' }}>
      {children}
    </span>
  );
}

Por qué daña tu aplicación:

  • Trabajo adicional de reconciliación: dado que se crea un objeto de estilo nuevo en cada renderizado, la lógica de comparación de React detecta una propiedad “cambiada” y modifica el nodo DOM aunque en realidad no haya habido cambio alguno.
  • CSS-in-JS más lento: las bibliotecas que analizan las declaraciones de estilo en línea pagan este costo repetidamente, empeorando el problema.
  • Puntos ciegos del compilador: el compilador de React 19 es capaz de memorizar componentes, pero no tiene forma de estabilizar por sí mismo las referencias a objetos en línea. Tan pronto detecta un objeto recién creado, omite optimizar esa parte.
  • Oportunidades de caché perdidas: los navegadores no pueden cachear las declaraciones de estilo en línea de la misma manera que cachean las hojas de estilo. Una clase CSS se analiza una sola vez, mientras que los estilos en línea se vuelven a analizar en cada montaje.

La solución:

// ✅ Good: CSS classes with dynamic data attributes
function Badge({ color, children }) {
  return (
    <span
      className="badge"
      data-color={color}
      style={{ '--badge-color': color }}
    >
      {children}
    </span>
  );
}
// CSS
// .badge { background-color: var(--badge-color); padding: 4px 8px; border-radius: 4px; }

Alternativamente, las utilidades de Tailwind CSS funcionan bien cuando se utilizan con cuidado con valores arbitrarios:

// Acceptable: Tailwind utility classes
function Badge({ color, children }) {
  return (
    <span className="px-2 py-1 rounded" style={{ backgroundColor: color }}>
      {children}
    </span>
  );
}

Mejor práctica para 2026: Integre el estilo estático en módulos CSS, clases Tailwind o un enfoque CSS-in-JS sin tiempo de ejecución. Reserve los estilos en línea para valores realmente dinámicos, como el ancho de una barra de progreso, y aun así, utilice propiedades personalizadas de CSS siempre que sea posible.

3. No utilizar correctamente la propiedad key en listas

El error: Depender del índice del array como clave, omitir por completo la propiedad key o generar una clave aleatoria en cada renderizado.

// ❌ Bad: Index as key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>
      ))}
    </ul>
  );
}
// ❌ Worse: Random key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={Math.random()}>{todo.text}</li>
      ))}
    </ul>
  );
}

Por qué daña tu aplicación:

  • Claves basadas en índice: cuando los elementos de la lista se reordenan, React asocia la misma clave a la misma posición y termina reutilizando el nodo DOM incorrecto para el elemento equivocado. Esto mezcla el estado de entrada, el foco y el estado interno de los componentes entre los elementos.
  • Claves generadas aleatoriamente: como se genera una nueva clave en cada renderizado, React desmonta y vuelve a construir cada nodo DOM cada vez que ocurre una actualización. Esto arruina el rendimiento y elimina cualquier estado de formulario en la lista.
  • Transiciones en React 19: con el renderizado concurrente y las transiciones activas, las claves defectuosas pueden causar distorsiones visibles en la interfaz de usuario y estados dañados cada vez que se interrumpe una actualización.

La solución:

// ✅ Good: Stable, unique identifier
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Si no hay un identificador único natural disponible, créelo cuando se generen los datos por primera vez y no en el momento de la renderización:

// At data creation, not in the component
const todos = items.map(item => ({
  ...item,
  id: crypto.randomUUID(), // or your backend's ID
}));

Mejor práctica para 2026: Cuando una lista proviene de un componente del servidor, asegúrese de que el propio servidor proporcione identificadores estables. Evite derivar claves a partir de la posición en el array, a menos que la lista sea realmente estática y solo crezca al agregar nuevos elementos.

4. Mutar directamente las props

El error: Tratar las props como si fueran variables locales mutables y modificarlas dentro de un componente hijo.

// ❌ Bad: Mutating props directly
function SortButton({ items }) {
  const handleSort = () => {
    items.sort((a, b) => a.price - b.price); // MUTATES the prop!
    onSort(items);
  };

  return <button onClick={handleSort}>Sort by Price</button>;
}

Por qué daña tu aplicación:

  • Corrupción silenciosa del componente padre: El estado del padre se modifica sin que se sepa nunca que ocurrió. La detección de cambios de React se basa en la comparación de referencias, por lo que la mutación pasa completamente desapercibida.
  • Errores por renderizado obsoleto: Dado que items sigue apuntando al mismo array en memoria, el componente padre nunca se vuelve a renderizar, lo que hace que la interfaz muestre datos desactualizados.
  • Confusión en el compilador de React: El compilador asume que los props fluyen de forma inmutable a través del árbol de componentes. Mutarlos directamente invalida su lógica automática de memorización.
  • Pérdida de previsibilidad: Cualquier otro componente que haga referencia al mismo array verá de repente la versión mutada y ordenada sin previo aviso.

La solución:

// ✅ Good: Immutable update
function SortButton({ items, onSort }) {
  const handleSort = () => {
    const sorted = [...items].sort((a, b) => a.price - b.price);
    onSort(sorted);
  };

  return <button onClick={handleSort}>Sort by Price</button>;
}

Mejor práctica para 2026: opte por Immer cuando las mutaciones anidadas parezcan inevitables, y establezca una regla de ESLint como prefer-readonly-props si utiliza TypeScript. Presuma que cada propiedad es readonly a menos que se demuestre lo contrario.

5. Demasiadas propiedades (en lugar de composición)

El error: crear “componentes divinos” extensos que aceptan una docena o más de propiedades solo para controlar cada variación interna posible.

// ❌ Bad: Prop explosion
function Card({
  title,
  subtitle,
  imageUrl,
  imageAlt,
  showImage,
  imagePosition,
  actions,
  showActions,
  actionAlignment,
  badge,
  badgeColor,
  onBadgeClick,
  footer,
  showFooter,
  footerContent,
  borderRadius,
  shadow,
  padding,
  variant,
}) {
  return (
    <div className={`card card--${variant}`} style={{ borderRadius, padding, boxShadow: shadow }}>
      {showImage && (
        <div className={`card__image card__image--${imagePosition}`}>
          <img src={imageUrl} alt={imageAlt} />
        </div>
      )}
      <div className="card__content">
        <h2>{title}</h2>
        {subtitle && <p>{subtitle}</p>}
      </div>
      {/* ... more conditional rendering ... */}
    </div>
  );
}

Por qué daña tu aplicación:

  • Una API frágil: Cada nueva exigencia implica agregar otro parámetro, modificar la estructura interna del componente y arriesgarse a que algo más falle.
  • Paquetes excesivamente grandes: El componente termina importando y procesando rutas de código para funcionalidades que en realidad nunca se utilizan.
  • Cobertura de pruebas complicada: Te ves obligado a validar una matriz cada vez mayor de combinaciones de parámetros.
  • Problemas con React Server Components: Las listas de parámetros sobrecargadas tienden a transferir más lógica al cliente de la necesaria.

La solución:

// ✅ Good: Composition
function Card({ children, className }) {
  return (
    <div className={clsx('card', className)}>
      {children}
    </div>
  );
}
function CardImage({ src, alt, position = 'top' }) {
  return (
    <div className={`card__image card__image--${position}`}>
      <img src={src} alt={alt} loading="lazy" />
    </div>
  );
}
function CardContent({ title, subtitle }) {
  return (
    <div className="card__content">
      <h2>{title}</h2>
      {subtitle && <p>{subtitle}</p>}
    </div>
  );
}
function CardActions({ children, align = 'end' }) {
  return (
    <div className={`card__actions card__actions--${align}`}>
      {children}
    </div>
  );
}
// Usage
<Card>
  <CardImage src="/photo.jpg" alt="Product" />
  <CardContent title="Headphones" subtitle="Noise cancelling" />
  <CardActions>
    <button>Add to Cart</button>
  </CardActions>
</Card>

Mejor práctica para 2026: utilice el patrón de componente compuesto o la composición basada en slots. Deje que el consumidor del componente decida sobre la estructura y el contenido; el propio componente solo debe proporcionar el contenedor.

6. No utilizar React.Fragment (Divs innecesarios)

El error: envolver elementos hermanos dentro de un simple <div> únicamente para cumplir con la regla de JSX de que un componente debe devolver un único nodo raíz.

// ❌ Bad: Wrapper div for layout that breaks semantics
function TableCell({ label, value }) {
  return (
    <div> {/* This div breaks the <table> structure! */}
      <td>{label}</td>
      <td>{value}</td>
    </div>
  );
}// ❌ Bad: Extra div in a list
function ListItems({ items }) {
  return (
    <ul>
      {items.map(item => (
        <div key={item.id}> {/* Invalid HTML: div inside ul */}
          <li>{item.name}</li>
          <li>{item.description}</li>
        </div>
      ))}
    </ul>
  );
}

Por qué daña tu aplicación:

  • Marcaje inválido: Un <div> no es un hijo válido de elementos como <table>, <ul>, <select> o <dl>. Los navegadores reescribirán silenciosamente el DOM para solucionarlo, lo que provoca incoherencias durante la carga del contenido en el lado del servidor.
  • Interferencia en el diseño: Los elementos de contenedor adicionales perturban el comportamiento de CSS Grid, Flexbox y la propiedad gap.
  • Fallas en la carga del contenido en React: Dada la popularidad actual de los métodos SSR por streaming y los componentes del servidor, este tipo de anidamiento inválido puede causar incoherencias fatales que hacen que la aplicación se cierre en el cliente.

La solución:

// ✅ Good: React.Fragment
function TableCell({ label, value }) {
  return (
    <>
      <td>{label}</td>
      <td>{value}</td>
    </>
  );
}
// ✅ Good: Fragment with key (required in lists)
function ListItems({ items }) {
  return (
    <ul>
      {items.map(item => (
        <React.Fragment key={item.id}>
          <li>{item.name}</li>
          <li>{item.description}</li>
        </React.Fragment>
      ))}
    </ul>
  );
}

Mejor práctica para 2026: utilice la abreviatura <> </> cuando no necesite una clave, y recurre a <React.Fragment key={...}> al renderizar arrays de fragmentos que sí la requieran. Revise periódicamente el árbol de componentes en React DevTools para detectar y eliminar los divs de contenedor innecesarios.

7. Renderizado de números falsos con && (errores ocultos)

El error: confiar en && para renderizar algo de forma condicional, sin tener en cuenta los casos en que el operando izquierdo evalúe a 0, NaN o a una cadena vacía.

// ❌ Bad: Shows "0" on screen when items.length is 0
function Cart({ items }) {
  return (
    <div>
      <h1>Shopping Cart</h1>
      {items.length && (
        <ul>
          {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
      )}
    </div>
  );
}
// ❌ Bad: accountBalance could be 0
function AccountSummary({ account }) {
  return (
    <div>
      {account.balance && <p>Balance: ${account.balance}</p>}
    </div>
  );
}

Por qué daña tu aplicación:

  • React en realidad renderiza 0: escribir {0 && <Component />} no produce nada, sino que muestra el número literal 0 como texto.
  • Defectos invisibles en la interfaz: los usuarios terminan mirando un “0” extraño en alguna parte de la página, lo cual parece defectuoso y puede alterar el diseño.
  • Valores cero en el mundo real: cosas como los saldos de cuenta, las cantidades de artículos o las puntuaciones suelen ser cero de manera válida y esperada. Tratar 0 como falso con && significa que o bien no se renderiza nada cuando debería hacerse, o bien se muestra el dígito en sí cuando no se desea.

La solución:

// ✅ Good: Explicit boolean check
function Cart({ items }) {
  return (
    <div>
      <h1>Shopping Cart</h1>
      {items.length > 0 && (
        <ul>
          {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
      )}
    </div>
  );
}
// ✅ Good: Nullish coalescing for defaults, ternary for conditionals
function AccountSummary({ account }) {
  return (
    <div>
      {account.balance != null ? (
        <p>Balance: ${account.balance}</p>
      ) : (
        <p>Loading balance...</p>
      )}
    </div>
  );
}

Mejor práctica para 2026: Evite por completo el uso de && para el renderizado condicional. Prefiera una estructura ternaria (condition ? : null) o establezca explícitamente un valor booleano con algo como Boolean(count) && .... Añada una regla de ESLint a su proyecto que identifique patrones de && riesgosos en JSX.

8. Omitir la memorización en JSX costoso

El error: Permitir que un subárbol costoso se vuelva a renderizar en cada actualización del padre, aun cuando las propiedades relevantes en realidad no hayan cambiado.

// ❌ Bad: HeavyChart re-renders every time Parent re-renders
function Dashboard({ data, filter }) {
  const [hoveredItem, setHoveredItem] = useState(null);

  return (
    <div>
      <Sidebar onHover={setHoveredItem} />
      <HeavyChart data={data} filter={filter} /> {/* Re-renders on every hover! */}
      <Tooltip item={hoveredItem} />
    </div>
  );
}
function HeavyChart({ data, filter }) {
  // Expensive SVG rendering, D3 calculations, etc.
  return <svg>{/* thousands of nodes */}</svg>;
}

Por qué daña tu aplicación:

  • Computación desperdiciada: HeavyChart vuelve a ejecutar su lógica de renderizado incluso cuando data y filter no han cambiado.
  • Pérdida de fotogramas: si el renderizado de HeavyChart tarda unos 50 ms, un simple evento de hover en una barra lateral puede reducir la tasa de fotogramas a aproximadamente 20 fps.
  • Límites del compilador de React: la memorización automática del compilador funciona dentro de un componente, pero no puede optimizar entre componentes si el padre sigue pasando referencias de callbacks recién creadas.

La solución:

// ✅ Good: Memoize the expensive component
const HeavyChart = memo(function HeavyChart({ data, filter }) {
  // Expensive rendering
  return <svg>{/* thousands of nodes */}</svg>;
});
function Dashboard({ data, filter }) {
  const [hoveredItem, setHoveredItem] = useState(null);

  // Stabilize the callback if passed to children
  const handleHover = useCallback((item) => setHoveredItem(item), []);

  return (
    <div>
      <Sidebar onHover={handleHover} />
      <HeavyChart data={data} filter={filter} />
      <Tooltip item={hoveredItem} />
    </div>
  );
}

Mejor práctica para 2026: el compilador de React 19 reduce la necesidad de memorización manual en casos sencillos, pero para subárboles realmente complejos, sigue siendo útil envolverlos con memo. Utilice el Profilador de React DevTools para confirmar dónde se encuentran los verdaderos cuellos de botella antes de recurrir a la memorización.

9. Combinar presentación y lógica de negocio

El error: crear un único componente que al mismo tiempo obtiene datos, los transforma, sigue el estado del formulario, ejecuta efectos secundarios y renderiza el marcado.

// ❌ Bad: God component
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [isEditing, setIsEditing] = useState(false);
  const [formData, setFormData] = useState({});

  useEffect(() => {
    fetchUser(userId).then(data => {
      setUser(data);
      setFormData(data);
      setLoading(false);
    });
  }, [userId]);

  const handleSubmit = async () => {
    await updateUser(userId, formData);
    setIsEditing(false);
  };

  if (loading) return <Spinner />;

  return (
    <div>
      <h1>{user.name}</h1>
      {isEditing ? (
        <form onSubmit={handleSubmit}>
          <input
            value={formData.name}
            onChange={e => setFormData({...formData, name: e.target.value})}
          />
        </form>
      ) : (
        <button onClick={() => setIsEditing(true)}>Edit</button>
      )}
    </div>
  );
}

Por qué daña tu aplicación:

  • Difícil de probar: no puedes verificar la lógica de obtención de datos sin montar todo el componente, y no puedes probar la interfaz gráfica renderizada sin simular la capa de red.
  • No reutilizable: el comportamiento del formulario está encerrado dentro de este único componente y no puede compartirse en otros lugares.
  • Difícil de comprender: el estado, los efectos secundarios y el marcado están entrelazados, por lo que un pequeño cambio en la interfaz puede dañar accidentalmente la lógica de manejo de datos.
  • Poco adecuado para los Componentes Servidor: según las convenciones de 2026, la obtención de datos debe realizarse en los Componentes Servidor, mientras que los Componentes Cliente deben reservarse para la interactividad.

La solución:

// ✅ Good: Separated concerns
// Container / Logic Hook
function useUserProfile(userId) {
  const { data: user, isLoading } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  const updateMutation = useMutation({
    mutationFn: updateUser,
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['user', userId] }),
  });

  return { user, isLoading, updateUser: updateMutation.mutate };
}
// UI Component
function UserProfile({ userId }) {
  const { user, isLoading, updateUser } = useUserProfile(userId);
  const [isEditing, setIsEditing] = useState(false);

  if (isLoading) return <Spinner />;

  return (
    <div>
      <h1>{user.name}</h1>
      {isEditing ? (
        <UserProfileForm
          initialData={user}
          onSubmit={(data) => {
            updateUser({ userId, ...data });
            setIsEditing(false);
          }}
          onCancel={() => setIsEditing(false)}
        />
      ) : (
        <button onClick={() => setIsEditing(true)}>Edit</button>
      )}
    </div>
  );
}
// Reusable Form Component
function UserProfileForm({ initialData, onSubmit, onCancel }) {
  const [formData, setFormData] = useState(initialData);

  return (
    <form onSubmit={(e) => { e.preventDefault(); onSubmit(formData); }}>
      <input
        value={formData.name}
        onChange={e => setFormData(prev => ({...prev, name: e.target.value}))}
      />
      <button type="submit">Save</button>
      <button type="button" onClick={onCancel}>Cancel</button>
    </form>
  );
}

Enfoque recomendado para 2026: Dividir las responsabilidades en ganchos personalizados para la lógica, componentes puramente presentacionales para el marcado y Componentes del Servidor para la carga de datos. Un componente debe realizar una acción o mostrar algo, y es raro que uno justifique hacer bien ambas cosas.

10. No utilizar interfaces de TypeScript para las props

El error: Depender de prop-types, de comentarios dispersos en JSDoc o, en el peor de los casos, omitir por completo la verificación de tipos de las props.

// ❌ Bad: No types
function Button(props) {
  return <button className={props.variant}>{props.children}</button>;
}
// ❌ Bad: PropTypes at runtime only
import PropTypes from 'prop-types';
function Button(props) {
  return <button className={props.variant}>{props.children}</button>;
}
Button.propTypes = {
  variant: PropTypes.string,
  children: PropTypes.node,
};

Por qué daña tu aplicación:

  • Fallas silenciosas: errores de escritura en los nombres de las propiedades, valores requeridos faltantes y tipos no coincidentes se manifiestan como caídas en tiempo de ejecución en lugar de ser detectados antes del despliegue.
  • El refactoring se vuelve complicado: renombrar una sola propiedad obliga a buscar manualmente en todo el código, mientras que un sistema de tipos indica de inmediato todos los lugares donde es necesario realizar actualizaciones.
  • Experiencia de desarrollo más deficiente: sin tipos, se pierden la autocompletación, las sugerencias de documentación integrada y la protección que brindan los renombramientos automáticos.
  • Alineación deficiente con React 19: las nuevas funcionalidades de React, como los Componentes de Servidor, el uso de use con Promesas y el reenvío de referencias, están diseñadas teniendo en cuenta el modelo de tipos de TypeScript; por lo tanto, omitir los tipos implica perder las garantías en tiempo de compilación en las que dependen estas características.

La solución:

// ✅ Good: Explicit TypeScript interface
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  children: React.ReactNode;
  type?: 'button' | 'submit' | 'reset';
}
function Button({
  variant = 'primary',
  size = 'md',
  disabled = false,
  onClick,
  children,
  type = 'button'
}: ButtonProps) {
  return (
    <button
      type={type}
      className={`btn btn--${variant} btn--${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

Enfoque recomendado para 2026: Habilitar el modo estricto (strict: true en tsconfig.json). Definir las propiedades del modelo con declaraciones interface en lugar de alias type, ya que las interfaces suelen generar mensajes de error más claros y son más fáciles de extender. Utilizar React.ComponentProps cuando sea necesario derivar tipos de elementos HTML nativos al crear componentes envolventes. Evitar utilizar any para los tipos de propiedades bajo ninguna circunstancia.

Resumen: La lista de verificación de componentes para 2026

Antes de fusionar un componente, revise esta lista de verificación:

  • [ ] ¿Elegiste HTML semántico en lugar de acumular elementos genéricos <div>?
  • [ ] ¿Los estilos están definidos en CSS en lugar de en objetos de estilo en línea?
  • [ ] ¿Los elementos de lista dependen de valores key estables y únicos?
  • [ ] ¿Se tratan siempre los props como de solo lectura?
  • [ ] ¿Puede el componente componerse en lugar de necesitar una docena o más de props de configuración?
  • [ ] ¿Reemplazó los divs de envoltura innecesarios por Fragment?
  • [ ] ¿Se escriben los renders condicionales como ternarios o con verificaciones booleanas explícitas en lugar de &&?
  • [ ] ¿Se memoriza el costoso JSX donde realmente es necesario?
  • [ ] ¿Está la lógica de negocio separada del marcado, alojada en hooks o funciones utilitarias?
  • [ ] ¿Están todos los props completamente descritos utilizando interfaces de TypeScript?

Ninguno de estos errores hará que tu aplicación falle desde el primer día. En cambio, la dañan gradualmente a través de una deuda de accesibilidad cada vez mayor, una pérdida de rendimiento sutil y una corrupción oculta del estado. Abordarlos ahora significa que tu códigobase de React resistirá la próxima actualización del framework, la próxima gran refactorización y cualquier equipo que eventualmente se haga cargo de mantener lo que has creado.

Leer más