Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ReactDOMNodeStream, adding streaming rendering. #10024

Merged
merged 17 commits into from
Jun 25, 2017
Merged

Conversation

aickin
Copy link
Contributor

@aickin aickin commented Jun 22, 2017

This PR builds on @tomocchino & @sebmarkbage's work on the server renderer in React 16, exposing the server renderer as a Node.js Readable Stream. This allows folks to pipe a renderer out to the network, and it's a fairly common request of folks using SSR.

The actual code part of it is pretty small, as the existing read() method of the server renderer in React 16 is already able to do partial/async rendering; all we needed to do was wrap that read() method in a Readable stream, which is pretty much all in src/renderers/shared/server/ReactDOMNodeStreamRenderer.js. The rest of the PR is code organization, build, test, and documentation changes.

I'm not entirely comfortable with the build changes, especially the changes in bundles.js, as I don't really know what things like fbEntry and hasteName mean. Names in general were a bit sticky, and I settled on ReactDOMNodeStream/react-dom/node-stream for the new object, mindful that it's possible that a renderer for web streams might be created in the future. I have some other ideas about naming after talking with @tomocchino, but I'll save those for another PR.

Thanks so much for all the work y'all do!

ETA: I should also give props to @spicyj, who I believe wrote an earlier version of the server renderer on top of which Tom and Sebastian built their work. Thanks, @spicyj!

@aickin
Copy link
Contributor Author

aickin commented Jun 22, 2017

Tests are working fine on my machine, but for some reason CI has ReactDOMServerIntegration-test failing every test when run in Fiber mode. I strongly suspect that this is because the require('react-dom/node-stream') line is failing in CI (it did in my first checkin, too), but it works fine locally on my machine.

I added a file at src/node_modules/react-dom/node-stream.js in order to provide react-dom/node-stream to the tests, but for some reason it doesn't seem to be working in CI. I would greatly appreciate any ideas about what to check or tweak; it's hard to debug CI remotely, and my knowledge of the build system is minimal.

ETA: I've confirmed that require('react-dom/node-stream') is failing in tests on CI, but I'm not yet sure why. I'm adding more console.log debugging to the test to try and understand what's going on in CI.

var React = require('react');
var ReactDOMPartialRenderer = require('ReactDOMPartialRenderer');

var Readable = require('stream').Readable;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's recommended to use readable-stream instead. This might cause wildly different behavior depending on the node version you use

Copy link
Contributor Author

@aickin aickin Jun 22, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the comment; I wasn't aware of readable-stream!

I did some reading on the subject, though (in particular, this blog post referenced by the readable-stream README), and it seemed like this is only really a problem for versions of Node <= 0.12. Given that those versions of Node are all deprecated, is there still a good reason to use readable-stream that you know of?

(and thanks again for your comment!)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey yeah glad you like it - readable stream contains fixes that aren't necessarily backported to older Node versions (a new version was published today which contains fixes for the ._destroy stuff iirc). Generally it's just a good practice to pin to a specific stream version rather than relying on whatever version is in core. Hope that makes sense (:

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any where we can read someone arguing against depending on the readable-stream package? This would make it easier to evaluate the counter-arguments for someone not too involved in the details. Seems non-obvious to me.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

E.g. how does it integrate with other code that assumes the native Node stream?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't found much other documentation or argumentation about readable-stream.

E.g. how does it integrate with other code that assumes the native Node stream?

My understanding (which is a bit fuzzy) is that it literally is the native Node stream code pulled out of Node core. The authors are the Node Streams Working Group.

I think the benefit of using readable-stream would be more consistency of behavior across versions of Node, potentially making it easier to repro reported bugs. Of course, Stream hasn't changed that much since 4.x (unlike in the 0.9-0.12 timeframe), so this may not be that big a deal.

On the other hand, linking to a particular version of readable-stream means that someone on the React team should probably track when readable-stream gets released and cut new releases of React if it's useful to upgrade. If we use native stream, though, developers don't have to wait on the React team to get the newest version of streams; they can choose to upgrade Node themselves.

For now, I vote we use stream; it's a simpler setup with less maintenance involved. If we find there are a bunch of streaming bugs due to Node versions, though, we can replace it with a pinned version of readable-stream. Does that sound OK?

@sebmarkbage
Copy link
Collaborator

This looks good to me.

I don't have a problem exposing this as an API but the strategy I had in mind for async component dependencies, coroutines and error boundaries would be a bit different in its performance characteristics.

I also think we'll want to expose multiple modes of rendering. E.g. one mode for plain simple HTML like we do today. Another mode for out-of-order "smart" rendering that inserts a bunch of script tags and other junk to do out-of-order stream rendering which can be faster than the simple mode. The naming convention should account for this.

ReactDOMNodeStream.renderToStream(element)
```

Render a React element to its initial HTML. This should only be used in Node.js; it will not work in the browser, since the browser does not support Node.js streams. React will return a [https://nodejs.org/api/stream.html#stream_readable_streams](Readable stream) that outputs an HTML string. The HTML output by this stream should be character-for-character equivalent to the output of [https://facebook.github.io/react/docs/react-dom-server.html#rendertostring](ReactDOMServer.renderToString). You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reverse the order of these links?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, you're right! Thanks!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fixed in 132567d. Thanks!

ReactDOMNodeStream.renderToStream(element)
```

Render a React element to its initial HTML. This should only be used in Node.js; it will not work in the browser, since the browser does not support Node.js streams. React will return a [https://nodejs.org/api/stream.html#stream_readable_streams](Readable stream) that outputs an HTML string. The HTML output by this stream should be character-for-character equivalent to the output of [https://facebook.github.io/react/docs/react-dom-server.html#rendertostring](ReactDOMServer.renderToString). You can use this method to generate HTML on the server and send the markup down on the initial request for faster page loads and to allow search engines to crawl your pages for SEO purposes.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The HTML output by this stream should be character-for-character equivalent to the output...

Phrasing is a little awkward, could we convey the same idea without "character-for-character" and "should"?

The HTML output by this stream will be exactly equal to what ReactDOMServer.renderToString would return.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ReactDOMServer.renderToString link should also be inside backticks

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestions! Should be fixed in b8f2529.


## Overview

The `ReactDOMNodeStream` class allows you to render your components in Node.js and stream the resulting markup.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it's not a class?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right you are!

Should be fixed in b6f9e06, and I opened PR #10027 to fix it in the doc for ReactDOMServer, too.

Thanks for the help!

aickin added a commit to aickin/react that referenced this pull request Jun 23, 2017
gaearon pushed a commit that referenced this pull request Jun 23, 2017
…ReactDOMServer is an object, not a class. (#10027)
@aickin
Copy link
Contributor Author

aickin commented Jun 23, 2017

So, I've fixed the CI build problem and got an LGTM from @sebmarkbage and reviews from @aweary & @gaearon. My understanding is that I am allowed to merge it now, but since I know this feature has been somewhat controversial in the past, I'll give a little more time (24 hours, say?) in case anyone else wants to chime in or wants more time to review.

Thanks to everyone who has looked at this so far!

* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactDOMNodeStreamRenderer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should live in the DOM folder. The only reason the other one wasn't is that we expect that most of the logic can be extracted as separate renderer and then move some parts out to DOM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 3d1421a. I assumed that the same logic applied to ReactDOMStringRenderer.js as well, since the file is parallel in structure to this one.

Thanks!

@@ -6,7 +6,7 @@
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule ReactServerRenderer
* @providesModule ReactDOMPartialRenderer
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's call this just "ReactPartialRenderer" for reasons mentioned above.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call. Done in 3d1421a. Thanks!

@xyzdata
Copy link

xyzdata commented Jul 20, 2017

ReactDOMNodeStream

streaming rendering.

https://github.com/aickin

#10024

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants