¿Cuál es la diferencia entre hydrate () y render () en React 16?

85

Leí la documentación, pero realmente no entendí la diferencia entre hydrate()y render()en React 16.

Sé que hydrate()se usa para combinar SSR y renderizado del lado del cliente.

¿Alguien puede explicar qué es hidratante y luego cuál es la diferencia en ReactDOM?

shabenda
fuente
a continuación, la respuesta dada por @tophar es correcta, si desea explorar más, puede leer este reactjs.org/docs/react-dom.html
Gorakh Nath

Respuestas:

80

De los documentos de ReactDOMServer (el énfasis es mío):

Si llama ReactDOM.hydrate()a un nodo que ya tiene este marcado renderizado por el servidor, React lo preservará y solo adjuntará controladores de eventos , lo que le permitirá tener una experiencia de primera carga muy eficaz.

El texto en negrita es la principal diferencia. renderpuede cambiar su nodo si hay una diferencia entre el DOM inicial y el DOM actual. hydratesolo adjuntará controladores de eventos.

Del problema de Github que se presentó hydratecomo una API separada :

Si este es su DOM inicial:

<div id="container">
    <div class="spinner">Loading...</div>
</div>

y luego llamar:

ReactDOM.render(
   <div class="myapp">
      <span>App</span>
   </div>,
   document.getElementById('container')
)

con la intención de hacer un renderizado solo del lado del cliente (no hidratación). Entonces terminas con

<div id="container">
   <div class="spinner">
       <span>App</span>
   </div>
</div>

Porque no arreglamos los atributos.

Para su información, la razón por la que no parchearon los atributos es

... Esto sería muy lento para hidratarse en el modo de hidratación normal y ralentizaría el renderizado inicial en un árbol sin SSR.

topher
fuente
8
No entiendo por qué el div renderizado no tiene div con el nombre de clase myapp y por qué la clase spinner está allí en el elemento renderizado final
pravin poudel
2
@pravinpoudel Creo que se debe a que no corrigen los atributos durante la renderización del lado del cliente. Es por eso que el atributo class="spinner"permanece como está en el <div>elemento.
Glenn Mohammad
29

Hydrate se usa básicamente en el caso de SSR (renderización del lado del servidor). SSR le brinda el esqueleto o el marcado HTML que se envía desde un servidor para que, por primera vez, cuando se cargue su página, no esté en blanco y los robots de los motores de búsqueda puedan indexarlo para SEO (un caso de uso de SSR). Entonces hydrate agrega el JS a su página o un nodo al que se aplica SSR. Para que tu página responda a los eventos realizados por el usuario.

Render se utiliza para renderizar el componente en el navegador del lado del cliente. Además, si intenta reemplazar el hidrato con render, recibirá una advertencia de que el renderizado está obsoleto y no se puede usar en caso de SSR. se eliminó debido a que es lento en comparación con la hidratación.

Sumit Kapoor
fuente
23

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.hydratey 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 hydratey render.

Estructura basica

  1. Un archivo que tiene el "esqueleto" HTML
  2. Un archivo con los componentes personalizados de React escritos en JSX
  3. Un script que genera todas las páginas para que las utilice el servidor.
  4. Un script para ejecutar el servidor

Resultados

Después de generar las páginas y ejecutar el servidor, voy a 127.0.0.1y 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.jstermina de cargarse, el loadevento se dispara desde windowy 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 exportsy Reactal principio del archivo).

// components.jsx

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 getPageque se exporta del archivo pageTemplate.js, que se muestra a continuación.

// genScript.js

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 getPagefunción mencionada anteriormente.

// pageTemplate.js

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

// server.js

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') {
    // artificial delay
    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');
Nathan Chappell
fuente
2
Guau. Básicamente respondiste la pregunta + explicaste cómo construir un generador estático mínimo como Gatsby. Asombroso. ¡Muchas gracias!
Sergey Lukin
22

Además de lo anterior ...

ReactDOM.hydrate()es igual que render(), pero se usa para hidratar (adjuntar oyentes de eventos) un contenedor cuyo contenido HTML fue renderizado por ReactDOMServer. React intentará adjuntar oyentes de eventos al marcado existente .

El uso de ReactDOM.render () para hidratar un contenedor renderizado por el servidor está obsoleto debido a la lentitud y se eliminará en React 17, así que utilícelo hydrate()en su lugar.

Devinder Suthwal
fuente
18

Todo el proceso de volver a poner la funcionalidad en el HTML que ya se renderizó en el lado del servidor React se llama hidratación.

Por lo tanto, el proceso de volver a renderizar sobre el HTML una vez renderizado se conoce como hidratación.

Entonces, si intentamos hidratar nuestra aplicación llamando, se ReactDOM.render()supone que debe hacerse llamando ReactDOM.hydrate().

Daniel
fuente
1

render eliminará cualquier cosa en el elemento especificado (llamado 'raíz' en la mayoría de los casos) y lo reconstruirá, mientras que hydrate mantendrá todo lo que ya esté dentro del elemento especificado y construirá a partir de eso, haciendo que la página inicial se cargue más rápido.

alvin lal
fuente