Is it possible to use the new renderToNodeStream?

Viewed 3818

I'm using server side rendering in a project, i just updated my stack to React 16.

My current stack is: Node 8.4 Express React 16 Redux Webpack Pug for static content (like header and footer)

i was wondering if is possible to use the new renderToNodeStream in my application using my stack.

to print a route, i'm using:

const matchedRoute = matchRoutes(routes, req.url);
let context = {};

if (matchedRoute) {
    const initialState = JSON.stringify(store.getState()).replace(/</g, '\\u003c');
    const criticalCSSPath = manifest['criticalCss.css'].replace('http://s.domain.com.br/', '/');
    const criticalCss = fs.readFileSync(`./public${criticalCSSPath}`, 'utf-8');

    if (context.url) {
        return res.redirect(302, context.url);
    }else {
        const content = renderToString(
            <Provider store={store}>
                <StaticRouter location={req.url} context={context}>
                    {renderRoutes(routes)}
                </StaticRouter>
            </Provider>
        );

        return res.render(view, {initialState: initialState, content, view, criticalCss});
    }
}

But if i want to render to nodestream, i need to do something like this, according to the documentation:

app.get("/", (req, res) => {
   res.write("<!DOCTYPE html><html><head><title>My Page</title></head><body>");
   res.write("<div id='content'>"); 
   const stream = renderToNodeStream(<MyPage/>);
   stream.pipe(res, { end: false });
   stream.on('end', () => {
      res.write("</div></body></html>");
      res.end();
   });
});

Does anyone know a way to achieve this using pug, like my current stack?

3 Answers

Here's an example

// Write the <head> and root <div>
res.write('<html><head>' + metaTags + '</head><body><div id="root>')
// Render the frontend React app
const stream = renderToNodeStream(<ReactWholeAppOrJustComponent/>)
// Pipe that HTML to the response
stream.pipe(res, { end: false });
// When React is finished, clean up the dangling HTML tags
stream.on('end', () => res.end('</div></body></html>'))

How about rendering your template as string and doing a split?

const html = pug.renderFile('path/to/file.pug', options);

now html is for example

"<!DOCTYPE html><html><body><div id='react'>ssrContent</div></body</html>"

then split, send the first part, stream, and send the rest.

html.split("ssrContent");

I had similar problems and I wrote a small function for ES6 template literals.

Example:

const streamString = require('node-stream-string')

// ...

app.get("/", (req, res) => {
    const stream = streamString`
        <!DOCTYPE html>
        <html>
        <head>
            <title>${ getTitle(req) }</title>
            <style>${ getStyle(req) }</style>
        </head>
        <body>    
            <div id='root'>
                ${ renderToNodeStream(<App/>) }
            </div>
        </body>
        </html>
    `
    stream.pipe(res)
})

Git: https://github.com/xelaok/node-stream-string

Related