react-prerendered-component
react-prerendered-component copied to clipboard
Plans for async cache get?
Hello, I've been playing around w/ your library and I think it'll work for my use case, aside from not being able to use an outside cache.
First I was wondering if you are currently thinking about adding this or opposed to it for reasons I'm not aware of currently.
Second, I was looking at the code and it seems that in ServedCachedLocation it could be extended to async load and render the portion needed (via local state, componentDidMount). I know there's an issue with this b/c it won't catch that subsequent render, but there may be ways to do such things here too. At the project I'm working on, we tag a function to indicate that ssr should wait, it's added to a queue of promises, then once all resolved it will trigger last render and send down final response. I may be totally off on this as I don't know the code very well yet, but wanted to see your thoughts.
Chris
First I was wondering if you are currently thinking about adding this or opposed to it for reasons I'm not aware of currently.
Yes, not not as you may expect.
ServedCachedLocation it could be extended to async load and render...that subsequent render...a queue of promises, then once all resolved
And this is something I am definitely don't want.
So. First of all, lets define what is "cache", and how it should behave:
- it should be fast
- it should be non-blocking
- it should not delay TTFB
- it should and could be only view layer cache
Your way of rendering data, especially waiting for all the promises to be resolved, is delaying TTFB, not letting you to send the first chunk of data once it is ready - you are waiting for EVERYTHING to be ready to display.
So, how I am going to (try) handle it in a future:
Cache
would have the following commands
-
get
(stream) - to get a cache -
set
(async) - to set a cache -
willGet
(sync) - to indicate that you are going read the cache, returns false if you shall not (and do the set instead)
During the render you have sync and fast willGet
, which lets you understand what you will do.
During the post process, you might set
cache in a non-blocking manner (I dont care)
During the post process, you might get
cache in a non-blocking manner, but it's not a promise, it's a stream, for you are not "waiting" for it, it will just start streaming once it ready.
There would be a helper to convert Promise to a stream.
During the post process, you might get
cache in a non-blocking manner, and it might return a new placeholder to be executed. Let's call it a sub-render
- an independent component to be injected into your code.
process(ReactDOM.renderToNodeStream(<App/>), {
microfrontends: { // :P
comments: async (props) => { const realProps = await loadData(props); return <Comments {... realProps} />},
similarVideos: (props) => <SilimarVideos {...props} />
}
});
So you are not waiting for all the promices - you are splitting your app into the set of isolated roots, able to render every single one as a standalone application, and render a composition of them.
The first render would be like:
<html><head>{HEADER}</head><body>{MENU}{MAIN}{COMMENTS}</body></html>
Then postprocess would simultaneusly trigger rendering of all this pieces, with <htmt><head>
already sent to the client, appeding return stream as soon as next chunk is ready.
In this case, if {COMMENTS}
need more time to get the data from the database, you will be able to send everything before to the client.
That would be another library(steam-machine), using an extended interface of this one to do the job.
TL;DR
- yes, you will have async get, to async get a
string value
or inject asubsequent render
, which could do whatever you want, including reading from a Redis. - yes, set is already async. I mean - I don't care about what it is.
-
no,
has
(willGet) has to be synchronous and FAST. -
may be, with server side Suspense you will be able to throw a promise from
get/willGet
and get what you want.
Plus:
- "Cache" here - is only a view level cache, it should be faster than direct React16 render, assessing remote caches, to determine should your render something or not, is not making this stuff "faster". This is a bit strange limitation, but it really should be that way.
- "Cache" here is not removing requirements to fetch the data in front of render, as long as you need that data in case there would be no cache (or to generate a cache key)
- "Subsequent render"(not available right now) would let you skip data fetching for the cached locations.
First of all thank you for taking the time for this detailed explanation. Very illuminating and I sincerely like the direction. The smaller the pieces, the better.
I've been recently looking into
re: waiting for TTFB we do stream parts of the response: header, body, scripts, etc, but it's in order. This proposal takes advantage of not needing order of rendering (which is what we have). My concern would be that we are bound quite a bit to SEO. That's why there are parts of the rendering that need to wait. Our application is really dynamic, but there's usually about half the page that needs to render HTML ready to be scanned. I'm curious how this would be affected by streaming partial bits later (unless that's not what you meant) as it wouldn't be necessarily a bot that can handle javascript (nor do we want them to).
BTW I got a good laugh at this quote: "Loadable components were the only library it can work with, but you are no longer besties." True story.
Again, thanks for your help. I'll be posting more questions I'm sure shortly.
-cf
waiting for TTFB we do stream parts of the response: header, body, scripts, etc, but it's in order
header, deferred scripts and styles, |THEN| title, body. It's natural to first write to a stream something you might write before the React render, and then pipe renderToStream
to res
. Subsequent render does the same recursively.
<html><head>{HEADER}</head><body>{MENU}{MAIN}{COMMENTS}</body></html>
Just decompose it to a multiple streams,
> <html><head>
> {HEADER}
> </head><body>
> {MENU}
> {MAIN}
> {COMMENTS}
> </body></html>
Stream composition was an alpha and omega, a base for all in nodejs system
// from https://www.npmjs.com/package/stream-concat
var stream1 = fs.createReadStream('file1.csv'); // ie ReactDom.renderToNodeStream.
var stream2 = fs.createReadStream('file2.csv');
var stream3 = fs.createReadStream('file3.csv');
var output = fs.createWriteStream('combined.csv');
var combinedStream = new StreamConcat([stream1, stream2, stream3]);
So - no "SEO" implications - you will render the page correctly and in the right order.
Got it. This makes sense. Thanks man.
cc //@ScriptedAlchemy