[CLJS] Server side rendering with Reagent


#1

Hi!

I play with Clojure and ClojureScript to build a personal project. I want to use an Express NodeJs server to render React components. In 2019, is there an idiomatic method to do that with Reagent or simply create Reagent component and render it to the server with ReactDOMServer.renderToNodeStream()?

Thanks!


#2

We use reagent.core/render-to-string. If you want to get a stream, you’ll have to do as you said: use reagent.core/as-element and then call ReactDOMServer.renderToNodeStream on it.


#3

@lilactown thanks for your answer. Yes, I plan to use as-element with ReactDOMServer.renderToNodeStream.

What is your method for fetching data on the server? I wanted to attach fetcher to the bidi route like this: {:component <component-to-render> :fetcher <function-to-fetching-data>}. fetcher can be a re-frame handler too.


#4

We do something a little crazy: we walk the component tree before we render it, and prefetch all the data that is required.

E.g. a component might look something like:

(defn greeting []
  (let [user-data (pull! :user-data [:first-name])]
    (fn []
      (when-not (:loading? @user-data)
        [:div "Hello, " (-> @user-data :data :first-name)]))))

In the browser, this function will render initially without any data and then reactively re-render when it’s fetched the user data.

On the server side, we have a function called prefetch that executes the entire component tree, and captures all of calls to pull!, waiting for the data to come back before continuing to walk the tree.

After the data has been fetched, it’s loaded into a cache and we call render-to-string using that cached data, so it’s already fully loaded when it renders to the string.

I’m not sure I would actually suggest going this route, because it has it’s downsides. The deeper your component tree gets, the longer your load times can become. This can be ameliorated by hoisting your fetches higher in the tree, but this can lead to over fetching and breaking encapsulation of your components.

I think that declarative data fetching on the server-side is still an open question and it’s not being pursued much in the Clojure(Script) community at large at the moment.