renderToString y StaticRouter en el servidor

Resumen

El server-side rendering con React empieza cuando envías tu aplicación como HTML desde el servidor y luego la hidratas en el cliente con JavaScript. Aprenderás a configurar renderToString y StaticRouter para generar esa primera instantánea, un paso clave si construyes apps universales con React y Node.

Qué es el server-side rendering en React y por qué importa

El flujo es simple de visualizar: el servidor envía un documento HTML, una especie de fotografía de tu aplicación, y luego React, ya en el navegador, le añade JavaScript para que sea interactiva. Ese segundo paso es lo que llamamos hidratación.

¿Qué es la hidratación en React? Es el proceso en el que React reutiliza el HTML que ya existe en el DOM y le conecta los event listeners y la lógica de JavaScript para volverlo interactivo, sin volver a renderizar desde cero.

La regla de oro acá: el servidor y el cliente deben renderizar exactamente lo mismo. Si no, React te lo reclama con un warning de mismatch.

Cómo preparar tu app de React para correr en el servidor

Lo primero es crear un archivo index.tsx dentro de la carpeta render. La extensión .tsx no es capricho: vas a usar JSX del lado del servidor y necesitas el soporte de TypeScript para React.

Dentro importas React y tu componente App. Y aquí viene el primer ajuste importante: el BrowserRouter no puede vivir dentro del componente App si quieres que funcione en ambos lados.

Por qué sacar BrowserRouter del componente App

El BrowserRouter solo entiende de rutas en el navegador, así que el servidor no puede usarlo. La solución es sacarlo del componente App y dejar a App envuelto en un React.Fragment para no perder el elemento contenedor.

Luego, en el cliente envuelves tu App con BrowserRouter, y en el servidor la envuelves con StaticRouter. Cada router en su contexto.

tsx import React from 'react' import { renderToString } from 'react-dom/server' import { StaticRouter } from 'react-router-dom/server' import App from '../frontend/containers/App'

export const render = (url: string) => { const html = renderToString( <StaticRouter location={url}> <App /> </StaticRouter> ) return template(html) }

Cómo usar renderToString y StaticRouter para generar HTML

La función renderToString de react-dom/server toma tu árbol de componentes y lo convierte en una cadena de texto HTML. Esa cadena es la instantánea que mandas al navegador.

StaticRouter necesita una prop llamada location, y ahí le pasas la URL de la petición (req.url). Así el router sabe qué fragmento de tu aplicación debe renderizar para esa ruta específica.

¿Cuál es la diferencia entre BrowserRouter y StaticRouter? BrowserRouter lee la URL del navegador en tiempo real, mientras que StaticRouter recibe la URL como prop y se usa solo en el servidor para una sola petición.

Si TypeScript te marca que no encuentra los tipos de react-dom, instálalos como dependencia de desarrollo:

bash yarn add -D @types/react-dom

Cómo conectar la función render con tu servidor Express

En tu servidor, donde antes llamabas a una función template, ahora llamas a render(req.url). El servidor reinicia, hace la petición y ya devuelve tu aplicación de React serializada como HTML.

Los pasos concretos quedan así:

  1. Crear render/index.tsx con la función render.
  2. Importar renderToString desde react-dom/server.
  3. Importar StaticRouter desde react-router-dom/server.
  4. Envolver App con StaticRouter y pasarle la location.
  5. Reemplazar la llamada a template por render(req.url) en el servidor.

Qué falta para completar el ciclo de hidratación

Hasta aquí el navegador recibe HTML, pero todavía no hay JavaScript ejecutándose. React no está vivo en el cliente, solo ves la estructura estática de la página.

Para cerrar el ciclo necesitas dos cosas: un bundle de JavaScript con tu aplicación de React generado por Webpack, y el proceso de hidratación que conecta ese bundle al HTML ya pintado.

Ese será el siguiente paso. ¿Ya tienes tu configuración de Webpack lista para el cliente? Cuéntame en los comentarios cómo te fue separando el BrowserRouter de tu App.