Inicio / Artículos / Fragmentos de React y StrictMode: marcado más ligero y detección temprana de errores

Fragmentos de React y StrictMode: marcado más ligero y detección temprana de errores

Aprenda cómo los fragmentos de React agrupan elementos sin necesidad de envoltorios DOM adicionales y cómo la doble invocación exclusiva para desarrollo de StrictMode revela renders e efectos impuros.

976 palabras

Dos características de React que nunca dibujan nada en la pantalla pero que influyen en casi todos los árboles de componentes son los Fragmentos y StrictMode. Los Fragmentos mantienen tu DOM libre de elementos contenedores que existen únicamente para satisfacer las necesidades de JSX, mientras que StrictMode somete intencionadamente a prueba tus componentes durante el desarrollo para que la lógica defectuosa se detecte antes de que lo hagan los usuarios.

Por qué JSX necesita una raíz única

JSX compila cada etiqueta en una llamada a función, por lo que dos etiquetas hermanas devueltas juntas representan dos valores cuando solo se espera uno. Esto impide la compilación:

return (
  <h1>Hello</h1>
  <p>Welcome</p>
);

La solución tradicional era envolver a las etiquetas hermanas en un div:

return (
  <div>
    <h1>Hello</h1>
    <p>Welcome</p>
  </div>
);

Ese nodo adicional conlleva costos: profundiza el DOM, interrumpe los diseños basados en flex o grid que esperan hijos directos, complica los selectores y puede generar HTML inválido o menos accesible.

Grupación sin contenedor

A Fragment agrupa hijos para React sin generar ningún elemento DOM. Su sintaxis sencilla consiste en un par de etiquetas vacías:

return (
  <>
    <h1>Hello</h1>
    <p>Welcome</p>
  </>
);

La forma explícita, React.Fragment, hace lo mismo y es necesaria cuando se debe pasar un prop:

return (
  <React.Fragment>
    <h1>Hello</h1>
    <p>Welcome</p>
  </React.Fragment>
);

El HTML generado contiene únicamente los elementos h1 y p. La mejora en rendimiento es mínima; el verdadero beneficio radica en un marcado correcto.

Devolver elementos hermanos

Un componente que genera varios hijos puede dejar el diseño en manos de su padre:

function Card() {
  return (
    <>
      <h2>Title</h2>
      <p>Description</p>
    </>
  );
}

Fragmentos con clave en listas

Al iterar sobre datos donde cada elemento genera más de uno, React sigue necesitando una clave estable para cada elemento. La sintaxis breve <> no permite atributos, por lo que se debe utilizar React.Fragment con una clave:

items.map(item => (
  <React.Fragment key={item.id}>
    <h2>{item.title}</h2>
    <p>{item.description}</p>
  </React.Fragment>
));

Celdas y filas de tabla

Las tablas HTML tienen un modelo de contenido estricto: un tr solo puede contener td o th. Un div de contención es inválido en este caso. Un Fragmento permite que un componente contribuya con varias celdas a una fila propiedad de su padre:

function Row() {
  return (
    <>
      <td>A</td>
      <td>B</td>
    </>
  );
}

Qué verifica StrictMode

StrictMode no renderiza nada, no agrega ningún elemento y no tiene efecto en las compilaciones de producción. En el desarrollo habilita verificaciones adicionales, entre ellas:

  • advertencias para métodos del ciclo de vida de clases obsoletas considerados inseguros, como componentWillMount
  • advertencias para APIs obsoletas como las referencias de cadena y findDOMNode
  • llamar dos veces a los cuerpos de componentes, inicializadores y funciones actualizadoras para detectar renderizado impuro
  • ejecutar efectos a través de un ciclo adicional de configuración, limpieza y reconfiguración al montar para identificar la falta de limpieza

La lista exacta ha cambiado a lo largo de las versiones de React, por lo que consulte la documentación actual correspondiente a su versión.

Renderizado doble intencional

Considere un componente que registra sus acciones mientras se renderiza:

function App() {
  console.log("Rendered!");
  return <h1>Hello</h1>;
}

Bajo StrictMode en el entorno de desarrollo, la consola muestra el mensaje dos veces:

Rendered!
Rendered!

Esto está diseñado así. Una función de renderizado debe ser pura: dado el mismo conjunto de propiedades y estado, debe devolver el mismo resultado sin modificar nada fuera de sí misma. Llamarla dos veces hace que las infracciones, como la modificación de una variable compartida, produzcan resultados claramente incorrectos. La pureza es importante porque el renderizado concurrente puede comenzar, pausarse, descartarse o repetirse, y el código que asume exactamente un renderizado por actualización falla en esas condiciones.

Activarlo

Rodee el árbol que desea verificar, generalmente toda la aplicación, en la raíz:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Efectos en StrictMode: el array de dependencias no es la solución

Aquí hay un efecto sin array de dependencias. Se ejecuta después de cada renderizado, por lo que cada actualización dispara otra solicitud:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
});

Agregar un array vacío limita la ejecución del efecto al momento de su montaje:

useEffect(() => {
  console.log("Fetching...");
  fetch("/api");
}, []); // stable dependency

Ese cambio es correcto, pero no evita las solicitudes duplicadas en el entorno de desarrollo. Desde React 18, StrictMode monta el componente, ejecuta su función de limpieza y lo vuelve a montar, por lo que un efecto con [] sigue ejecutándose dos veces allí. La ejecución duplicada es una señal, no un error. La verdadera solución es una función de limpieza que haga que la segunda ejecución sea inofensiva, generalmente abortando la primera solicitud con un AbortController o ignorando su resultado con una bandera. Un efecto de este tipo también es seguro ante montajes reales en producción.

Junto a lado

  • Propósito: Los Fragmentos agrupan elementos; StrictMode detecta errores.
  • Salida del DOM: ninguno de los dos agrega un elemento.
  • Efecto en producción: ninguno para ninguno de los dos.
  • Comportamiento en tiempo de ejecución: Los Fragmentos se renderizan normalmente; StrictMode invoca dos veces las operaciones de renderizado y los efectos solo en modo desarrollo.
  • Beneficios: marcado más limpio y válido frente a código predecible y seguro de efectos secundarios.

Una configuración mínima para probar

El componente a continuación devuelve dos elementos hermanos a través de un Fragmento:

export default function App() {
  return (
    <>
      <h1>Hello World</h1>
      <p>Rendered using Fragments</p>
    </>
  );
}

El archivo de entrada lo renderiza dentro de StrictMode, por lo que cualquier lógica impura que se agregue posteriormente se detecta de inmediato en modo desarrollo:

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Puntos clave

  • Utilice un Fragmento siempre que exista un elemento contenedor únicamente para satisfacer las necesidades de JSX, y emplee React.Fragment con un key en listas.
  • Mantenga StrictMode activado durante el desarrollo; los registros y efectos duplicados son diagnósticos intencionales.
  • Corrija los efectos que se ejecutan dos veces mediante una limpieza adecuada, y no modificando las dependencias.
  • Lecturas relacionadas