I\'ve read the documentation, but I didn\'t really understand the difference between hydrate() and render() in React 16.
I know hydrate()
I don't have anything specific to add to what's been said above about the use of hydrate, but in trying to learn about it I put together a little example, so here's the work for whoever finds it helpful.
Serve two pages, one which uses ReactDOM.hydrate and one which uses ReactDOM.render. They will depend upon some react components written in JSX, which are loaded by tags, given artificial delay (by the server) to illustrate the difference between hydrate and render.
After I generate the pages and run the server, I go to 127.0.0.1 and am presented with the header hydrate, a button, and two links. I can click the button, but nothing happens. After a few moments, the document finishes loading and the button starts counting my clicks. Then I click on the "render" link. Now, the page I'm presented with has the header render and two links, but no button. After a few moments, the button appears and is immediately responsive.
On the "hydrate" page, all the markup is immediately rendered, because all the necessary html is served with the page. The button is unresponsive because there are no callbacks connected yet. Once components.js finishes loading, the load event fires from the window and the callbacks are connected with hydrate.
On the "render" page, the button markup isn't served with the page, but only injected by ReactDOM.render, so it isn't immediately visible. Note how the appearance of the page is jarringly changed by the script finally loading.
Here is the custom react component I am using. It will be used by the server in node with react to statically render components, and it will also be loaded dynamically from the server for use in pages (this is the purpose of checking for exports and React objects at the beginning of the file).
// 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 (
);
}
exports.MyButton = MyButton;
This is the script used to generate all the pages required for the server. First, babel is used to transpile components.jsx into javascript, then these components are used, along with React and ReactDOMServer, to create the actual pages. These pages are created with the fuction getPage which is exported from the file pageTemplate.js, shown next.
// 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);
This file just exports the getPage function mentioned previously.
// pageTemplate.js
exports.getPage = function(
reactElementTag,
reactElementString,
reactDOMMethod
) {
return `
${ reactDOMMethod }
${ reactElementString }
hydrate
render
`;
}
Finally, the actual server
// 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');