Open fionawhim opened 7 years ago
LMK if you’d like me to explore implementing this sort of strategy.
Forgive me if this is too forward, but I gave it a bit of a try here: https://github.com/finneganh/rakt/tree/ssr-data in server.js (and the new walkTree.js)
What I have there is walking the component tree and pausing at each Data
component to run its “mod” and put it in the cache, then continuing the tree walk with the new data available. The cache is then re-used for the final renderToString
so all the components can render with their actual data into the HTML.
I believe that, besides making the SSR HTML include the components rendered with data, this introduces caching for for @initial
components that aren’t tied to Route
s, and also supports if sub-Route
s are rendered based on @initial
results of parent components.
Ideally in this approach the list of “chunk” hashes to pre-load would also get derived from what modules were visited during the tree walk, but I haven’t implemented that.
I don’t know if my skepticism around working with pre-parsed routes is warranted; it feels like it goes against the spirit of react-router v4, which makes me worry that it won’t support some possible ways that v4 is used. I’m not sure what your intention is, whether you feel that it’s ok long-term or if the pre-parsing of routes is just a temporary solution.
So I see how the solution works; it's similar to stuff like react-resolver. And to be clear, it should always still be possible to incorporate libraries/concepts like that with rakt; as you discovered, if you walk the tree that's rendered, you can prefill the cache, and then do one final render before sending the response. I'll make the cache api more pleasant in the future to enable this sort of thing.
However, one of the key ideas behind this lib is to be able to statically parse out routes and data/code dependencies, to be used for preloading chunks, etc. Specifically, this also prevents waterfall requests (detailed explanation here - https://medium.com/@taion/react-routing-and-data-fetching-ec519428135c)
Thankfully, the experience isn't too bad when you can't parse it out (eg - with a dynamic pathname, or when there's no route path pointing to a module) - in that case, it makes the call from the browser instead, and loads it like a regular async component. I do want to make the DX around this a little better.
The SSR code in server.js looks like it could benefit from the approach Apollo uses in its apollo-react package, in the
getDataFromTree
function.The approach is to recursively instantiate each component and call its
render
method to get new children to walk. If any component defines a special method to load initial GraphQL queries, the tree walker awaits the promise returned by that method.The goal here is to prime the Apollo Redux store with all of the initial queries so that when the
renderToString
method is fired off, the components will have their data at the ready and render synchronously.I wonder if a similar approach could work for rakt, calling the
@initial
blocks and caching their results.I think the benefit would be to remove the need to match the react-router routes on the server, which may be fragile or introduce limitations in how you can use it.
I think this scheme could also push to a list of chunks as it walks the tree, so the chunk script tags could still be written out as they are now.