Skip to content

Commit 3274774

Browse files
authored
Merge pull request #59 from Ibadichan/update-react-streaming-ssr-example
Updated react streaming ssr example
2 parents c76f742 + 485595d commit 3274774

File tree

1 file changed

+102
-35
lines changed

1 file changed

+102
-35
lines changed

README.md

+102-35
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,52 @@ In case or React rendering you may use **interleaved streaming**, which would no
190190
similar how StyledComponents works
191191
192192
```js
193-
import {discoverProjectStyles, createLink, createStyleStream} from 'used-styles';
194-
import MultiStream from 'multistream';
193+
import express from 'express';
194+
import {
195+
discoverProjectStyles,
196+
loadStyleDefinitions,
197+
createCriticalStyleStream,
198+
createStyleStream,
199+
createLink,
200+
} from 'used-styles';
201+
202+
const app = express();
195203

196204
// generate lookup table on server start
197-
const stylesLookup = discoverProjectStyles('./build'); // __dirname usually
205+
const stylesLookup = isProduction
206+
? discoverProjectStyles('./dist/client')
207+
// load styles for development
208+
: loadStyleDefinitions(async () => []);
209+
210+
app.use('*', async (req, res) => {
211+
await stylesLookup;
212+
213+
try {
214+
const renderApp = (await import('./dist/server/entry-server.js')).default;
215+
216+
// create a style steam
217+
const styledStream = createStyleStream(stylesLookup, (style) => {
218+
// _return_ link tag, and it will be appended to the stream output
219+
return createLink(`dist/${style}`) // <link href="dist/mystyle.css />
220+
});
221+
222+
// or create critical CSS stream - it will inline all styles
223+
const styledStream = createCriticalStyleStream(stylesLookup); // <style>.myClass {...
224+
225+
await renderApp({ res, styledStream });
226+
} catch (err) {
227+
res.sendStatus(500);
228+
}
229+
});
230+
```
231+
232+
```js
233+
// entry-server.js
234+
import React from 'react';
235+
import { renderToPipeableStream } from 'react-dom/server';
236+
import MultiStream from 'multistream';
237+
import { Readable } from 'node:stream';
238+
import App from './App';
198239

199240
// small utility for "readable" streams
200241
const readableString = string => {
@@ -205,37 +246,52 @@ const readableString = string => {
205246
return s;
206247
};
207248

208-
async function MyRender() {
209-
// render App
210-
const htmlStream = ReactDOM.renderToNodeStream(<App/>)
211-
212-
await stylesLookup;
213-
// create a style steam
214-
const styledStream = createStyleStream(stylesLookup, (style) => {
215-
// _return_ link tag, and it will be appended to the stream output
216-
return createLink(`dist/${style}`) // <link href="dist/mystyle.css />
217-
});
218-
219-
// or create critical CSS stream - it will inline all styles
220-
const styledStream = createCriticalStyleStream(stylesLookup); // <style>.myClass {...
221-
222-
// allow client to start loading js bundle
223-
res.write(`<!DOCTYPE html><html><head><script defer src="client.js"></script>`);
224-
225-
const middleStream = readableString('</head><body><div id="root">');
226-
const endStream = readableString('</head><body>');
227-
228-
// concatenate all steams together
229-
const streams = [
230-
middleStream, // end of a header, and start of a body
231-
styledStream, // the main content
232-
endStream, // closing tags
233-
];
234-
235-
MultiStream(streams).pipe(res);
236-
237-
// start by piping react and styled transform stream
238-
htmlStream.pipe(styledStream);
249+
const ABORT_DELAY = 10000;
250+
251+
async function renderApp({ res, styledStream }) {
252+
let didError = false;
253+
254+
const { pipe, abort } = renderToPipeableStream(
255+
<React.StrictMode>
256+
<App />
257+
</React.StrictMode>,
258+
{
259+
onShellError() {
260+
res.sendStatus(500);
261+
},
262+
onAllReady() {
263+
res.status(didError ? 500 : 200);
264+
res.set({ 'Content-Type': 'text/html' });
265+
266+
// allow client to start loading js bundle
267+
res.write(`<!DOCTYPE html><html><head><script defer src="client.js"></script></head><body><div id="root">`);
268+
269+
const endStream = readableString('</div></body></html>');
270+
271+
// concatenate all streams together
272+
const streams = [
273+
styledStream, // the main content
274+
endStream, // closing tags
275+
];
276+
277+
new MultiStream(streams).pipe(res);
278+
279+
// start by piping react and styled transform stream
280+
pipe(styledStream);
281+
},
282+
onError(error) {
283+
didError = true;
284+
console.error(error);
285+
}
286+
},
287+
);
288+
289+
setTimeout(() => {
290+
abort();
291+
}, ABORT_DELAY);
292+
}
293+
294+
export default renderApp;
239295
```
240296
241297
**!! THIS IS NOT THE END !!** Interleaving links and react output would break a client side rehydration, as long as _
@@ -244,9 +300,20 @@ injected_ links were not rendered by React, and not expected to present in the "
244300
You have to move injected styles out prior rehydration.
245301
246302
```js
303+
import React from 'react';
247304
import { moveStyles } from 'used-styles/moveStyles';
305+
import ReactDOM from 'react-dom/client';
306+
import App from './App';
307+
308+
// Call before `ReactDOM.hydrateRoot`
309+
moveStyles()
248310

249-
moveStyles();
311+
ReactDOM.hydrateRoot(
312+
document.getElementById('root'),
313+
<React.StrictMode>
314+
<App />
315+
</React.StrictMode>
316+
);
250317
```
251318
252319
You might want to remove styles after rehydration to prevent duplication. Double check that corresponding _real_ CSS is

0 commit comments

Comments
 (0)