What React developers should copy from cljs

Hey I wrote a blog post to convince my team of react devs to copy the right things that clojurescript has pioneered (and ignore all the object orientation pitfalls that we see everywhere else)

I also wrote an example app that shows a plain react style that look surprisingly similar to re-frame, leveraging recoiljs https://github.com/frankiesardo/buzzer-multiplayer

Happy to hear your thoughts

1 Like

Nice! That’s v.similar to what I ended up with on my last js contract a few years ago - apart from recoil, we had redux and some other selector thing - recoil looks better. Reading the code that manually creates new versions of state (bc lacking persistent data structures) brings back memories!

What I miss most in js land though is the repl. vscode had some kind of eval window as I recall, and I found this thing called quokka which got part of the way to quickly eval something. what do you do as a js dev with no repl? what’s your dev strategy?

Hehe, yeah, persistent ds are sorely missed. But on the other hand the manual hand-waving makes it easier to optimise React equality checks by reference, which makes it faster (if you generate two deeply nested data structures in ClojureScript that are functionally equal but point at different objects, comparing them might be slow).

Good point on the repl, but with figwheel / hot reload I tend to use it less. React Fast Refresh is decent when it works and the advantage of vanilla js is that you can pop into the browser console and easily evaluate stuff. The React Dev Tools give you a lot of leverage, like changing component state on the fly.

The thing I miss the most is devcards actually. I used it a lot as a tool to communicate with designers and check edge cases and I couldn’t find anything similar for js.

yeah the other tools are useful - but they’ll take the repl out of my cold dead hands :wink:

I had used https://storybook.js.org/ for a similar thing as devcards

I always thought cljs react libs did reference equality checks? - dug out the blog post https://swannodette.github.io/2013/12/17/the-future-of-javascript-mvcs/. but I suppose there can be cases in om/reagent etc where reference equality is false but = is true and the ui needn’t be re-rendered, but is re-rendered

As it turns out, the default shouldComponentUpdate implementation is extremely conservative, because JavaScript devs tend to mutate objects and arrays! So in order to determine if some properties of a component have changed, they have to manually walk JavaScript objects and arrays to figure this out.

Instead of using JavaScript objects, Om uses ClojureScript data structures which we know will not be changed. Because of this, we can provide a component that implements shouldComponentUpdate by doing the fastest check possible - a reference equality check. This means we can always determine if the paths changed, starting from the root, in logarithmic time.

A well aged wine from 2013 :slight_smile:

If I recall correctly at that time om was doing root -> children re-rendering passing all the props explicitly (like my second example in the blog post). So let’s say you changed something under the “z” key in the app db

{:a {...} :b {...} :c {:x {...}, :y {...}, z: {..}}

and you need to re-render the application state again.

Using om and persistent ds at that time meant all the components requiring data on other paths (a, b, x, y) didn’t need to re-render, but all the parents (root, "c:, “z”) did. It wasn’t trivial to implement the same in react.

At lot has changed since then (Pure Components, memoized functions…). And yeah, like you say: the reference equality s the same if you do copy on write js or assoc in cljs, but if think you can rely on clojure equality and create new objects (e.g. into {} you’ll hit some problems). And ultimately doing root -> children re-render for every state change proved to be too slow for om (despite the cart rocket metaphor). re-frame subscriptions solved the problem of targeted updates very nicely.

:slight_smile: thanks for the update