Fork me on GitHub
#reagent
<
2023-06-23
>
scknkkrer12:06:33

Hello fellas, Let’s say I wanna render some piece of React in Back-End with Shadow-CLJS (NodeJS application), what is the highly-overview steps of this in order to render it to string, so handler can send it?

Roman Liutikov12:06:44

normally it would be the following: compile cljs project to js, have code that runs ReactDomServer/renderToString, return the string from web endpoint handler

scknkkrer12:06:51

But I’m all in the JS environment, end-to-end. Doesn’t it help me to make things less painfull? Like, I wanna do it all in REPL, but I couldn’t even run the module since it requires React, it gives me error:

import React, { useContext, useRef, useEffect, createElement, useState, useMemo, Fragment, useCallback } from 'react';
^^^^^^

SyntaxError: Cannot use import statement outside a module
    at internalCompileFunction (node:internal/vm:73:18)
    at wrapSafe (node:internal/modules/cjs/loader:1175:20)
    at Module._compile (node:internal/modules/cjs/loader:1219:27)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1309:10)
    at Module.load (node:internal/modules/cjs/loader:1113:32)
    at Function.Module._load (node:internal/modules/cjs/loader:960:12)
    at Module.require (node:internal/modules/cjs/loader:1137:19)
    at require (node:internal/modules/helpers:121:18)
    at /Users/sckn/projects/closed-source/lunara/cls/.shadow-cljs/builds/lunara.cls.backend/dev/out/cljs-runtime/shadow.js.shim.module$$ionic$react.js:3:38
    at global.SHADOW_IMPORT (/Users/sckn/projects/closed-source/lunara/cls/target/backend/main.js:64:44)

Roman Liutikov12:06:47

eehmm, that's a different question now

Roman Liutikov12:06:50

JS stuff is stateful, I wouldn't bother spending time to make sure it works with REPL in Node

scknkkrer12:06:20

But it has to run in NodeJS (right?), it’s just re-frame with some initialization. No interaction at all. I will cover that effectfull things later with wrappers.

scknkkrer12:06:22

Also, I already have a compiled version while I’m developing, can’t I inject that result of mine, simply?

Roman Liutikov12:06:24

It's not clear what your problem is about

scknkkrer12:06:39

Oh, sorry. I already have a seperate builds for my BE and FE, and they run smoothly. I need some SSR (Server-Side Rendering). When I try to require my namespace of reagent components/pages, it fails as it is up there.

Ben Lieberman17:06:04

Given this trivial example class component, I gather (from perusing docs and GitHub issues) that the first form is preferred:

(defn Rando []
  (let [rand (r/atom (js/Math.random))]
    (r/create-class {:reagent-render (fn [_] [:div @rand])
                     :display-name "Rando"})))
as opposed to:
(defn Rando []
  (r/create-class {:constructor (fn [this props] (r/set-state this {:num (js/Math.random)
                   :render (fn [this] [:div (-> this r/state :num)])}))}))
Is there a strong reason to prefer one over the other, when using Form-3 components?

rads18:06:09

I think I'd just use a Form-2 component for the first case. The Form-3 components are usually only needed for lower-level interop with JS or React

Ben Lieberman18:06:35

Yeah, in general I would do the same. I was just curious about the structure of Form 3s in general. In the Reagent GH docs it mentions that it's not a good idea to use the :render key, and instead the :reagent-render key should be used.

rads18:06:13

The :reagent-render makes more sense when you want to create a class component to add lifecycle methods as opposed to using r/set-state. I can't think of a real use case for r/set-state today now that everyone is writing React components with hooks anyways

Ben Lieberman18:06:22

This is all just for my own edification anyway, but using :render still allows for lifecycle methods, no?

rads18:06:22

That's right

2
rads18:06:26

I remember now that error boundaries are still implemented as classes in React and that's where r/set-state may still be useful. In that case I'd personally prefer your first example since r/atom is the default way of managing local state in a Reagent app. It would be less one thing to think about in a codebase if you can avoid using the setState API altogether in favor of r/atom

Ben Lieberman19:06:17

Also what's interesting to me about the second example is that equivalent code in React would be an error, I think. You can't use setState in an unmounted component, and if it's being constructed, it hasn't been mounted (I think?)

Ben Lieberman19:06:44

I also know quite little about React though, so maybe that's not true