No tengo nada específico que agregar a lo que se dijo anteriormente sobre el uso de hydrate
, pero al tratar de aprender al respecto, reuní un pequeño ejemplo, así que aquí está el trabajo para quien lo encuentre útil.
Objetivo
Sirva dos páginas, una que usa ReactDOM.hydrate
y otra que usa ReactDOM.render
. Dependerán de algunos componentes de reacción escritos en JSX, que se cargan mediante <script>
etiquetas, dado un retraso artificial (por parte del servidor) para ilustrar la diferencia entre hydrate
y render
.
Estructura basica
- Un archivo que tiene el "esqueleto" HTML
- Un archivo con los componentes personalizados de React escritos en JSX
- Un script que genera todas las páginas para que las utilice el servidor.
- Un script para ejecutar el servidor
Resultados
Después de generar las páginas y ejecutar el servidor, voy a 127.0.0.1
y se me presenta el encabezado hidratar , un botón y dos enlaces. Puedo hacer clic en el botón, pero no pasa nada. Después de unos momentos, el documento termina de cargarse y el botón comienza a contar mis clics. Luego hago clic en el enlace "render". Ahora, la página que se me presenta tiene la representación del encabezado y dos enlaces, pero ningún botón. Después de unos momentos, aparece el botón y responde de inmediato.
Explicación
En la página "hidratar", todo el marcado se procesa inmediatamente, porque todo el html necesario se sirve con la página. El botón no responde porque aún no hay devoluciones de llamada conectadas. Una vez que components.js
termina de cargarse, el load
evento se dispara desde window
y se conectan las devoluciones de llamada con hydrate
.
En la página "renderizar", el marcado del botón no se sirve con la página, solo se inyecta por ReactDOM.render
, por lo que no es visible de inmediato. Observe cómo la apariencia de la página cambia de manera discordante debido a que el script finalmente se carga.
Fuente
Aquí está el componente de reacción personalizado que estoy usando. Será utilizado por el servidor en el nodo con reaccionar para renderizar componentes estáticamente, y también se cargará dinámicamente desde el servidor para su uso en las páginas (este es el propósito de comprobar los objetos exports
y React
al principio del archivo).
var exports = typeof(exports) == 'object' ? exports : {};
var React = typeof(React) == 'object' ? React : require('react');
function MyButton(props) {
[click, setClick] = React.useState(0);
function handleClick() { setClick(click + 1); }
return (
<button onClick={handleClick}>Clicked: {click}</button>
);
}
exports.MyButton = MyButton;
Este es el script que se utiliza para generar todas las páginas necesarias para el servidor. Primero, se usa babel para transpilar components.jsx en javascript, luego estos componentes se usan, junto con React y ReactDOMServer, para crear las páginas reales. Estas páginas se crean con la función getPage
que se exporta del archivo pageTemplate.js
, que se muestra a continuación.
let babel = require('@babel/core');
let fs = require('fs');
let ReactDOMServer = require('react-dom/server');
let React = require('react');
let pageTemplate = require('./pageTemplate.js');
script = babel.transformFileSync(
'components.jsx',
{presets : [['@babel/react']]}
);
fs.writeFileSync('components.js',script.code);
let components = require('./components.js');
hydrateHTML = pageTemplate.getPage(
'MyButton',
ReactDOMServer.renderToString(React.createElement(components.MyButton)),
'hydrate'
);
renderHTML = pageTemplate.getPage(
'MyButton',
'',
'render'
);
fs.writeFileSync('hydrate.html',hydrateHTML);
fs.writeFileSync('render.html',renderHTML);
Este archivo solo exporta la getPage
función mencionada anteriormente.
exports.getPage = function(
reactElementTag,
reactElementString,
reactDOMMethod
) {
return `
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js" defer></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" defer></script>
<script src="./components.js" defer></script>
</head>
<body>
<h1>${ reactDOMMethod }</h1>
<div id="react-root">${ reactElementString }</div>
<a href="hydrate.html">hydrate</a>
<a href="render.html">render</a>
</body>
<script>
window.addEventListener('load', (e) => {
ReactDOM.${ reactDOMMethod }(
React.createElement(${ reactElementTag }),
document.getElementById('react-root')
);
});
</script>
</html>
`;
}
Finalmente, el servidor real
let http = require('http');
let fs = require('fs');
let renderPage = fs.readFileSync('render.html');
let hydratePage = fs.readFileSync('hydrate.html');
let componentsSource = fs.readFileSync('components.js');
http.createServer((req, res) => {
if (req.url == '/components.js') {
setTimeout(() => {
res.setHeader('Content-Type','text/javascript');
res.end(componentsSource);
}, 2000);
} else if (req.url == '/render.html') {
res.end(renderPage);
} else {
res.end(hydratePage);
}
}).listen(80,'127.0.0.1');