97 lines
2.5 KiB
JavaScript
97 lines
2.5 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
*/
|
|
|
|
import * as React from 'react';
|
|
// import {renderToString} from 'react-dom/server';
|
|
import {renderToPipeableStream} from 'react-dom/server';
|
|
import App from '../src/App';
|
|
import {DataProvider} from '../src/data';
|
|
import {API_DELAY, ABORT_DELAY} from './delays';
|
|
|
|
// In a real setup, you'd read it from webpack build stats.
|
|
let assets = {
|
|
'main.js': '/main.js',
|
|
'main.css': '/main.css',
|
|
};
|
|
|
|
module.exports = function render(url, res) {
|
|
const data = createServerData();
|
|
// This is how you would wire it up previously:
|
|
//
|
|
// res.send(
|
|
// '<!DOCTYPE html>' +
|
|
// renderToString(
|
|
// <DataProvider data={data}>
|
|
// <App assets={assets} />
|
|
// </DataProvider>,
|
|
// )
|
|
// );
|
|
|
|
// The new wiring is a bit more involved.
|
|
res.socket.on('error', error => {
|
|
console.error('Fatal', error);
|
|
});
|
|
let didError = false;
|
|
const {pipe, abort} = renderToPipeableStream(
|
|
<DataProvider data={data}>
|
|
<App assets={assets} />
|
|
</DataProvider>,
|
|
{
|
|
bootstrapScripts: [assets['main.js']],
|
|
onAllReady() {
|
|
// Full completion.
|
|
// You can use this for SSG or crawlers.
|
|
},
|
|
onShellReady() {
|
|
// If something errored before we started streaming, we set the error code appropriately.
|
|
res.statusCode = didError ? 500 : 200;
|
|
res.setHeader('Content-type', 'text/html');
|
|
pipe(res);
|
|
},
|
|
onShellError(x) {
|
|
// Something errored before we could complete the shell so we emit an alternative shell.
|
|
res.statusCode = 500;
|
|
res.send('<!doctype><p>Error</p>');
|
|
},
|
|
onError(x) {
|
|
didError = true;
|
|
console.error(x);
|
|
},
|
|
}
|
|
);
|
|
// Abandon and switch to client rendering if enough time passes.
|
|
// Try lowering this to see the client recover.
|
|
setTimeout(abort, ABORT_DELAY);
|
|
};
|
|
|
|
// Simulate a delay caused by data fetching.
|
|
// We fake this because the streaming HTML renderer
|
|
// is not yet integrated with real data fetching strategies.
|
|
function createServerData() {
|
|
let done = false;
|
|
let promise = null;
|
|
return {
|
|
read() {
|
|
if (done) {
|
|
return;
|
|
}
|
|
if (promise) {
|
|
throw promise;
|
|
}
|
|
promise = new Promise(resolve => {
|
|
setTimeout(() => {
|
|
done = true;
|
|
promise = null;
|
|
resolve();
|
|
}, API_DELAY);
|
|
});
|
|
throw promise;
|
|
},
|
|
};
|
|
}
|