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

Q: Server rendering? #36

Closed
stevoland opened this issue Aug 12, 2015 · 13 comments
Closed

Q: Server rendering? #36

stevoland opened this issue Aug 12, 2015 · 13 comments
Labels

Comments

@stevoland
Copy link

It seems to me that this would require an API to serialize the store state and rehydrate on the client otherwise the initial client render may differ from the server rendered one.

Is this feature planned?

Thanks

@voideanvalue
Copy link
Contributor

Actually, it's already mostly implemented and we do use it internally at Facebook. Just that the hooks aren't exposed currently and it is undocumented at this point. We do plan to expose and document in the near future.

In the meanwhile, for the curious, here's how it works on a high level:

Instead of sending a serialized version of the store from the server along with the markup and then rehydrating the store on the client, we send the query responses that we used for the initial render on the server. The client can then use this response to build a store identical to the one that was built on the server and re-conciliate the render. Do note that this means that the server and the client must generate exactly the same initial query.

And these are the pieces of code you need to know about to make it work:

  • GraphQLQueryRunner.run optionally accepts a fetchMode argument that it passes on to RelayPendingQueryTracker. Note that RelayStore.primeCache that is called by the internals of RelayRootContainer currently doesn't accept this.
  • When fetchMode is DliteFetchModeContstants.FETCH_MODE_PRELOAD, (RelayPendingQueryTracker creates a promise for the pending query, but doesn't actually fetch it)[https://github.com/facebook/relay/blob/2a86be3e71cdc6511fa994e3de539f72070da1b4/src/store/RelayPendingQueryTracker.js#L87-L90]. So if you send code that calls RelayPendingQueryTracker.resolvePreloadQuery with the results of the queries, along with the initial markup, Relay can rebuild the store and re-conciliate the render.

We actually have a wrapper internally that allows us to do all this. The technical preview, in its current form, would require some modification to make this work. I'll try to write an example when I find some free time.

Side note: This actually allows us to do another interesting thing, apart from just server rendering the entire application. We can boost the performance of a client-side rendered application by actually starting the fetching of the queries on the server with the initial request while starting a render with fetchMode being DliteFetchModeContstants.FETCH_MODE_PRELOAD on the client and streaming down the results of the queries as they're available. We call it, surprise surprise, the preload mode.

To compare, here is what a somewhat simplified sequence of events looks like:

Client Only:

Client: Request            Parse, Call React.render  Request for Queries                      Render
Server:          Response
Server:                                                                   Process  Response

Completely server side:

Client: Request                                                   Parse, Re-conciliate render
Server:          Process Queries, React.renderToString, Response

Preload:

Client: Request            Parse, Call React.render         Render
Server:          Response, Process Queries, Stream Results

@taion
Copy link
Contributor

taion commented Aug 12, 2015

That preload mode sounds amazingly cool.

Is there anything between server-side and preload, where e.g. some set of initial requests that are "fast" to render and generate the core of the page are rendered entirely on the server, while slower requests are streamed to the client after the initial client render?

@pkieltyka
Copy link

The fast / slow async loading sounds overly complex and instead async stuff should be implemented on client side as needed.

On Aug 12, 2015, at 4:37 PM, Jimmy Jia [email protected] wrote:

That preload mode sounds amazingly cool.

Is there anything between server-side and preload, where e.g. some set of initial requests that are "fast" to render and generate the core of the page are rendered entirely on the server, while slower requests are streamed to the client after the initial client render?


Reply to this email directly or view it on GitHub.

@taion
Copy link
Contributor

taion commented Aug 12, 2015

@pkieltyka It's a perfectly reasonable way to handle server-side rendering with Flux - set an aggressive timeout on the server, let some portion of the requests succeed there, and then have clients re-request all the missing data. It lets you split the difference between showing your user a mostly-blank page, and with letting everything be held up by the slowest request.

@voideanvalue
Copy link
Contributor

Actually, you can start fetching all queries on the server, stream the results to the client and have the client do an initial render with only a subset of all the queries and then re-render as and when more query results arrive :). Look at Ready State (ready v/s done) and how GraphQLQueryRunner changes it.

@taion
Copy link
Contributor

taion commented Aug 12, 2015

Oh, got it - very cool!

@yungsters
Copy link
Contributor

However, if you look closely, you might notice that we do not currently have a way to get a ready state of {ready: true, done: false}, though. The DefaultNetworkLayer that we released yesterday does not have the ability to receive streaming responses. We didn't think it was important enough to hold up getting the public release out there. :X

@th0r
Copy link

th0r commented Aug 13, 2015

@voideanvalue But how do you set proper HTTP response code in the "preload" case? For example, user asked for missing resource and you should send 404, but you've sent 200 in the initial response because you haven't fetched your data yet.

@stevoland
Copy link
Author

@voideanvalue Thanks for the thorough response, really appreciated!

I'll try to write an example when I find some free time.

That would be fantastic!

@joshduck
Copy link

@th0r We'd enforce full server rendering if we might want the HTTP response to return a non-200 response code. We would typically only want to do this for bots though.

@davide-ganito
Copy link

Hi everyone!
I did't get how to render the markup server side. I have to load the store in some way before calling React.renderToString()?
Thx!

@josephsavona
Copy link
Contributor

@davide-ganito as @voideanvalue mentioned, Relay supports server rendering but:

the hooks aren't exposed currently and it is undocumented at this point. We do plan to expose and document [them] in the near future.

@josephsavona
Copy link
Contributor

Thanks for asking about this! We'll track progress of server-side rendering support in #136

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

No branches or pull requests

10 participants