This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2019-05-27
Channels
- # announcements (2)
- # beginners (85)
- # boot (4)
- # calva (4)
- # cider (14)
- # cljdoc (8)
- # cljs-dev (5)
- # cljsrn (10)
- # clojure (101)
- # clojure-europe (1)
- # clojure-italy (6)
- # clojure-nl (12)
- # clojure-spec (4)
- # clojure-uk (71)
- # clojurescript (119)
- # core-async (20)
- # cursive (1)
- # datascript (2)
- # duct (3)
- # emacs (19)
- # fulcro (150)
- # graphql (1)
- # hoplon (2)
- # instaparse (2)
- # jobs (1)
- # jobs-discuss (11)
- # joker (9)
- # luminus (6)
- # lumo (1)
- # off-topic (33)
- # onyx (1)
- # quil (1)
- # re-frame (23)
- # reagent (11)
- # robots (2)
- # rum (6)
- # sql (1)
- # test-check (10)
- # unrepl (1)
(js/web3.eth.Contract. param1 param2)
Does anyone here know how reacts DomServer works? It looks to me like it just walks the component tree and renders them as html elements. Does it have any special behaviours beyond this though, that would be hard to recreate? In particular does it do anything funky like render special attributes into the DOM of components? The background is that I’m implementing SSR from clojure code that serialises a component DOM from reagent flavoured hiccup, and I’m hydrating those components client side with reagent. It appears to me to work for my simple case, but a colleague is concerned that it might not work on more complex cases… mainly based on the assertion that react DOM server is a lot of code. I’ve also checked in chromes web developer tools, and it looks like when I rehydrate my component client side there is no superfluous DOM render made… i.e. it looks to me like providing the component state is the same as it was on the server that it diffs the DOM properly and doesn’t cause a re-render.
Yeah… I borrowed my SSR rendering code from here: https://yogthos.net/posts/2015-11-24-Serverside-Reagent.html and it seems to work with reagent and a recent react.
it puts a data-reactid
into each component
though I don’t think that’s necessary these days
@rickmoynihan Look at Rum’s serialiser, everything you need is there https://github.com/tonsky/rum/blob/gh-pages/src/rum/server_render.clj
I’m cautious of rum because it’s on an ancient version of react. And I think react has itself changed to do less over time here… but tracing the history, and what it actually does at any point is quite hard.
React’s SSR haven’t changed much as well, Rum produces HTML string that is fully compatible with react-dom/server output
Note that here I’m talking only about serialization, you’ll still have to take care of proper execution of lifecycle methods etc
Is this implementation not enough? https://yogthos.net/posts/2015-11-24-Serverside-Reagent.html
This one produces HTML string which is incompatible with newer React versions, React’s SSR no longer requires data-reactid
attributes
Yeah I think data-reactid
is no longer required…
My question is really what is required, and in what way is the above not compatible? As it seems to work for my simple example.
From brief overview it doesn’t seem to call componentWillMount which should be called when rendering on server
why is that important? I shouldn’t need to care about what is called on the server; only that I generate the right output, no?
(my knowledge of reactjs is tiny though)
you might have data fetching in componentWillMount or basically any side effect that might be called when component is about to render
it looks like componentWillMount is deprecated
Reagent is still using it
And it’s not really deprecated , it’s considered to be unsafe in concurrent mode. Reagent won’t work in that new rendering mode anyway since it’s relying on multiple unsafe APIs
hmmm interesting 👀
ok, backing up a bit… requirements wise my ideal solution would be to: 1. Have a way to do SSR rendering of cljc components rendered via a clojure backend. 2. Allow selective clientside interop with pure reactjs components… accepting that these component trees will not be SSRable without stubbing the js components. I’m thinking in the few cases we’d do this, this could be done with spinners etc. 3. It would be useful to run a recent react version. 4. For interop reasons it would be convenient to be as close to react as possible, using the new react hooks API ( https://juxt.pro/blog/posts/react-hooks-raw.html ) where possible; but reagentified hiccup instead of jsx in cljc components h/t @dominicm
So I’m wondering if I’m asking the impossible; it feels like it should be doable.
So looking at the above some more, would I not just need a way to stub hooks with mocks/no-ops on the server side? Excepting perhaps an implementation of useState
, that returns the initial state?
Hmmm, ok I’ve got this weird behavior:
=> (let [a (to-array [:original])
v (apply vector a)]
(aset a 0 :modified)
(= :original (nth v 0)))
false
apply vector
when passed an array behaves like vec
and uses the array as the PV backing array.wow that is weird
v
is not so immutable after all
@rickmoynihan the rum library has a CLJ hiccup-to-html-string feature that is compatible with React’s SSR
hooks throws a bit of a wrench, you have to decide how to handle that kind of stuff
@lilactown: you mean for SSR in clj?
yeah — I’ve been looking into that
do you not just need to shim useState
, and essentially no-op the other hooks?
hehe me neither… only just get my head into react
what kind of effects would you fire SSR? The context is pretty different in the server, is it not?
the other thing I’m thinking is, if we don’t want hooks to be used in Clojure then maybe it’s better to not stub them
yeah definitely… I was thinking the same, that most if there is no serverside analog would be stubbed out by a reader conditional :thumbsup:
the great benefit of CLJC is reader conditionals. perhaps it’s better to be explicit?
one of the downsides I’ve experienced using Node.js for SSR is things sometimes work, sometimes not depending if you’re on the server vs browser which can confuse people
@lilactown: you’re the author of hx right? What changes would be needed to it to make it work with ssr?
honestly it’s been on my plate long enough and people have asked for it, I should just do it
@roman01la convinced me 😜
I’m just trying not to computer as much this week so might not get to it. if you’re interested in contributing what you’re doing to hx, I’d be interested in reviewing it!
Well I might be interested — but I know next to nothing about react or the clojurescript ecosystem around it
so just trying to figure it out
@lilactown when you say interpreter, what do you mean? An interpreter for writing dom nodes?
by interpreter I mean the thing that takes hiccup like [my-component {:style {:color “green”}} “foo”]
and turns it into react-compatible HTML <div style=“color: green”>foo</div>
:thumbsup: yeah
It feels very much like the lisp curse that every clj(s|c) react library has to pretty much write their own one of those
I’ve been looking at hicada today, and it turns out it doesn’t do the ssr case of this… and reagent implements one that is pure cljs only etc…
but that’s too great a feat for me :P and I ended up taking it in a different direction
uix has a neat way of extending props processing to custom attributes, so you can implement things like a :css
prop that will create a CSS class for you using a CSS-in-JS lib
I’m not sure those small differences are the reasons people do it… it seems mainly like it’s because of fundamental issue’s with other libraries; but I could be wrong
writing parsers / interpreters for something like hiccup is such a good programming problem in that it’s a fairly contained, understood problem
it’s fun to implement 🙂 but as a user there’s a paradox of choice — or worse, you pick up one assuming they’re more or less equivalent to each other… and then run into a limitation, like not running in clj
Anyway do we not just need one of these that works well enough in all the appropriate contexts? It seems like if we had that we can just use raw react, and compose libraries together rather than build big frameworks that wrap react and fail to keep up with it.
yes, but inevitably people want more and there’s an ease to building it all into one library
like I said, hx started as just a hiccup library but I found it too annoying to use just functions as React components, so I wrote the defnc
macro
what does that macro do exactly?
In PHP, they run a node server and send code to it for doing server side rendering. I think you could do that in clojure using nashorn.
haven’t experimented but I fear for the amount of complexity that involves 😬 but it’s an unfounded fear
Yeah I’ve thought about it too; but I’m not sure it’s a good option.
also nashorn is dead
never tried it but JNI fills me with fear
so you’d probably need to use GraalVM
@rickmoynihan defnc
basically takes your component like this:
(defnc MyComponent [{:keys [foo children]}]
(if foo
[:div “foo” children]
[:div “not foo” children]))
and turns it into:
(defn MyComponent [props]
(let [{:keys [foo children]} (props->clj props)]
(hx.hiccup/parse (if foo
[:div “foo” children]
[:div “not foo” children]))))
ok so nothing fancy — seems totally reasonable too
for instance, Dan Abromov is working on some new hot loading features in React itself that a macro like defnc
could plug into so that your component didn’t lose it’s state when your code is reloaded
One thing that I think might be problematic from clojurescript, is not having the linter
and being able to enforce usage on hooks
so perhaps a solution to that
yes my point exactly
though I think it’s the reverse
i.e. I think you need to enforce that it’s not used outside of the macro
but that should be doable too
eh, I think it’s better to let people fail there perhaps. but help people avoid putting hooks inside of a loop
/`map`/etc.
or have an actual linter take care of that if we ever get the level of adoption necessary
there’s not a good way to enforce use of hooks outside a macro without making things really weird for interop
I recon you could do it easily enough
not sure yet… was thinking you could do something like 1. force all hooks to be wrapped with a special function that performs the check. 2. have the macro walk the syntax tree looking for symbols that ref vars which have that metadata on them, and have them pass a special arg to the wrapper that indicates the call is allowed. Though thinking about it this might not work… as cljs doesn’t have vars… but then clj does and clj implements the macro…. hmmm…
eitherway I recon you do something
one thing you can actually do is inspect the stack, and check the call is made from the right stackframe… i.e. that your macro is the immediate parent to the call.
though that might be brittle
in order to do it at compile-time, and ensure that all hooks are only called inside a defnc
macro, you would have to do a pass on the entire app
because you also have to take care of custom hooks:
;; is this a hook, a component, or an error?
(defn foo []
(useState “foo”))
yeah was going to say… unless you never import hooks, and have your macro present them to the user as a binding, which you can enforce is never leaked. Though it wouldn’t work for custom ones.
I recon it’s not worth it 🙂