Fork me on GitHub
#clerk
<
2023-01-28
>
jaydeesimon03:01:35

Hello! I am trying to use a React component, https://www.jsdelivr.com/package/npm/@nivo/line in my Clerk notebook and write a viewer. I used the https://github.com/nextjournal/clerk/blob/main/notebooks/js_import.clj as inspiration. I feel like I’m close but now I’m not sure how to combine the React component from the ES module and Reagent via SCI to render it in the browser. Here’s what I have so far 🧵

jaydeesimon03:01:00

(def nivo-line-viewer
  {:transform-fn clerk/mark-presented
   :render-fn
   '(fn [_]
      [nextjournal.clerk.render/with-dynamic-import
       {:module ""}
       (fn [NivoLine]
         ;; adapt-react-class not available :(
         [(reagent/adapt-react-class NivoLine)
          {:data {"put some data" "here"}}])])})

jaydeesimon03:01:25

I elided much of the actual code to make it easier to read but this is what it renders

jaydeesimon03:01:37

I learned that the adapt-react-class in Reagent is not available so I might be stuck or have to take a different route

jaydeesimon03:01:44

I don’t have a ton of experience in the JS world so I’m hoping someone will notice I’m doing something terribly wrong and point me in the right direction

jaydeesimon03:01:50

Thanks in advance!

Sam Ritchie03:01:14

Try [:> NivoLine …]

Sam Ritchie03:01:29

That syntax lets you use a react class directly in a reagent component tree

Sam Ritchie04:01:17

@U098UL4QP on my phone so I can’t test it but I hope that will get it done!

borkdude12:01:19

Do you need to wrap this in an html call? Not sure... cc @U5H74UNSF

mkvlr12:01:28

@U098UL4QP do you have a simple working limo + reagent example that you’re trying to port to Clerk?

mkvlr12:01:52

I think you need to use one of the props, not the object directly

mkvlr12:01:01

(ns scratch
  (:require [nextjournal.clerk :as clerk]))

(def nivo-line-viewer
  {:transform-fn clerk/mark-presented
   :render-fn '(fn [{:keys [data]} _]
                 [nextjournal.clerk.render/with-dynamic-import
                  {:module ""}
                  (fn [Nivo]
                    (js/console.log Nivo)
                    [:> Nivo {:data (clj->js data)}])])})

(clerk/with-viewer nivo-line-viewer
  {:data [{:x 1 :y 1}
          {:x 2 :y 3}
          {:x 3 :y 2}]})

mkvlr12:01:22

if I use (.-Line Nivo) I get `null is not an object (evaluating 'P.current.useRef')`

jaydeesimon12:01:06

@U5H74UNSF ahhh ok this is where my React/JS experience is failing me. I do have an example of using it in a shadow-cljs project that I am trying to port. I think the prop we need is ResponsiveLine

jaydeesimon12:01:53

Here's the docs that may be more useful https://nivo.rocks/line/. There's a code tab that has a JS example

jaydeesimon12:01:46

My example I'm trying to port does exist but in a private repo. This may not be that useful but this is the Reagent component https://gist.github.com/jaydeesimon/7844e272b5d43f6a9b14f28ac469f767

mkvlr12:01:09

let me know if you figure it out

mkvlr12:01:31

you have the example above working in plain reagent?

mkvlr12:01:43

I’m afk for a bit…

jaydeesimon12:01:56

Yup I do have it. What I'll do next is extract out a minimum working version working in Reagent and stick that in a public repo. Then it'll be easier for me or experts like you all to port that to a Clerk viewer version. I'm also gonna be afk for a bit but I'll report back on this thread. Thanks @U017QJZ9M7W @U04V15CAJ @U5H74UNSF!

jaydeesimon21:01:20

I was able to throw together a working example of a rendered @nivo/line graph using shadow-cljs: https://github.com/jaydeesimon/nivoline-example, if this helps

jaydeesimon21:01:24

After playing around this for a bit more trying to use dynamic import, I’m getting the feeling that the issue has something to do with the dependencies of @nivo/line and them not being available in the dynamic import context. But honestly, really not sure

Sam Ritchie21:01:13

@U098UL4QP what do you think of trying out the template at https://github.com/nextjournal/clerk-cljs-demo, the clerk/custom one?

Sam Ritchie21:01:04

that will let you compile your own js and get those components loaded into sci without dynamic import

Sam Ritchie21:01:35

I’ve got a line on a way to do this with much less effort, but this is basically a tighter version of what I’ve been doing for my docs notebooks

jaydeesimon21:01:55

That looks like a great idea! That was going to be my next question: how can I just take what I've got and drop Clerk in it and figure out the rendering from there? Looks like this is the answer to that question

jaydeesimon21:01:48

@U017QJZ9M7W I will give that a go 👍

borkdude21:01:14

@U098UL4QP if you want to debug the dynamic import, try console.log the dynamic imported library - it may very well be that you need to read another field like .-default

Sam Ritchie21:01:21

Based on the example I think that’s true

Sam Ritchie21:01:43

@U098UL4QP the dynamic import gets you the namespace, basically

borkdude21:01:32

I can try in the squint playground

borkdude21:01:32

(def nivo (js/await (js/import "")))
(js/console.log nivo)

borkdude21:01:05

If you want to use ResponsiveLine from the module, you need to do:

[:> (.-ResponsiveLine Nivo) ...]
Not [:> Nivo ...]

borkdude21:01:27

I'm also getting react.production.min.js:25 Uncaught TypeError: Cannot read properties of null (reading 'useRef') when I try (.-ResponsiveLine Nivo)

jaydeesimon21:01:27

Right I tried that and same as you, that's the error I get as well that I'm not sure how to make sense of TypeError: Cannot read properties of null (reading 'useRef') :thinking_face:

borkdude21:01:06

Perhaps an issue in clerk? It looks like some kind of hooks stuff...

jaydeesimon21:01:24

Yeah the error message seems pretty common if you google it but not sure how to trace it back to a hypothesis or if it's a red herring

Sam Ritchie21:01:55

useRef comes from “react”, so maybe react needs to be dynamically loaded too

Sam Ritchie21:01:15

weird but I guess that error comes from react 🙂

borkdude21:01:21

I can reproduce this in the scittle playground as well:

borkdude21:01:18

oh wait, there is something with "function components" in reagent, maybe that's it

Sam Ritchie21:01:49

I think that is only if you write a clojure function that uses useEffect, useRef etc…

Sam Ritchie21:01:03

js react components can use all of that stuff and :> should be fine

borkdude21:01:09

:f>
didn't help indeed.
(defn my-component []
    [:div
     (when @nivo
       (let [rl (.-ResponsiveLine @nivo)]
         [:div [:f> rl {}]]))])

borkdude22:01:12

This code is literally in that nivo lib:

export const useMeasure = () => {
    const measureRef = useRef(null)

borkdude22:01:17

when I set a breakpoint there, it's exactly where it crashes

Sam Ritchie22:01:36

so however react gets resolved in the dynamic import is not working import { useRef, useState, useEffect } from 'react'

borkdude22:01:20

You think useRef itself is null? That's not what I thought when I saw the error message

borkdude22:01:48

or do you think it pulls in a wrong version of react?

Sam Ritchie22:01:02

no I think it’s doing something like react.useRef , where react is null

borkdude22:01:21

but then the import statement would already crash

borkdude22:01:25

I would think

Sam Ritchie22:01:28

I had some issue before where I was bundling mathbox, and in the bundled version it replaced all “three” imports with a global THREE

Sam Ritchie22:01:58

but then that was a major pain when I tried to do the dynamic import thing, I had to nest the dynamic requires, and then assign the imported THREE to window.THREE before dynamically importing mathbox

Sam Ritchie22:01:18

it was all so confusing that I went the other make-cljs-libraries-and-clerk-templates-with-custom-js route

borkdude22:01:12

yes, the issue here is that when dynamically importing things with dependencies, it gets pretty complicated, especially when you mix and match different versions of react

borkdude22:01:44

you can however use import maps for this, but probably not on the level of clerk viewer functions

Sam Ritchie22:01:48

@U04V15CAJ I wonder if there is a non-esm version of this library that bundles everything. then it would package its own version of react, which is a bummer, but might actually work, which is a plus

borkdude22:01:20

let's try..

Sam Ritchie22:01:31

maybe as easy as removing the esm suffix?

borkdude22:01:02

I don't think this library supports this use case

Sam Ritchie22:01:25

haha what a mess

Sam Ritchie22:01:28

@U098UL4QP I would recommend a custom JS build here

Sam Ritchie22:01:09

it’s been very pleasant to work with Clerk that way, I’ll have something even better this week but the project that the clerk/custom template here generates https://github.com/nextjournal/clerk-cljs-demo is pretty good imo

Sam Ritchie22:01:03

basically you get this template going, and then you can make these components available inside your viewers. happy to help in any way you want if you decide to go this route

Sam Ritchie22:01:10

happy to help if you don’t, for that matter 🙂

😁 2
thanks3 2
jaydeesimon22:01:12

Really appreciate you all spending the time to help unblock me. Looks like pointing it to a CDN won’t work in this case but thanks for helping me confirm that. @U017QJZ9M7W, I will go the custom template route as that looks like what I’m looking for. Also, it looks like the custom template case will work for any JS library which is where I’d like to be eventually

jaydeesimon22:01:22

I’ll try it out in the next couple of days and report back

jaydeesimon22:01:16

Also, I just wanted to express some gratitude for Clerk. It’s been a game changer for me in terms of understanding problems and communicating that understanding to other people. It’s such a great tool so I’ve been doubling down on my understanding of it. I look forward to its progress and would be happy to contribute in any way I can 🙌

🙌 6
🎉 4
🖤 6
Sam Ritchie13:01:06

@U098UL4QP if you want to hack together on this, I’ve got something that can work more easily for you I think without the whole custom template;

Sam Ritchie13:01:38

once this is merged https://github.com/mentat-collective/clerk-utils/pull/10, clerk-utils will have versions of watch! and build! that can take care of all of the custom cljs stuff for you. If you have a Clerk project that you want to work with nivo/line, if you point me to it I’d be happy to put up a PR getting it working

🙏 4
jaydeesimon14:01:30

Hey @U017QJZ9M7W. This sounds great. I don’t yet have a project up with Clerk + nivo/line but I was planning to get one up today. What I can do is set that project up and have clerk-utils be a dependency and use the sha of your sritchie/watcher branch or main whenever it gets merged

jaydeesimon14:01:36

Then I’ll post a link to the project here and see if I can get it working. Will likely reach out for help

Sam Ritchie14:01:40

don’t suffer too much as I haven’t written docs yet; BUT I did convert mafs.cljs to use the new stuff, again still in a branch https://github.com/mentat-collective/Mafs.cljs/pull/7/files

Sam Ritchie14:01:49

but the upshot is, you have versions of clerk/serve! and clerk/build! that take a :cljs-namespaces key, and if you pass a sequence of symbols for namespaces you want to compile, everything will Just Work, no extra setup required. I use them like this. https://github.com/mentat-collective/Mafs.cljs/blob/015e088c7f82da816b0afc09e56fec348bc7cd7e/dev/user.clj

awesome 2
borkdude14:01:40

@U017QJZ9M7W Maybe you can write a clerk README about this

Sam Ritchie14:01:43

here I am including a single cljs file: https://github.com/mentat-collective/Mafs.cljs/blob/015e088c7f82da816b0afc09e56fec348bc7cd7e/dev/mafs/clerk_ui.cljs and then from there I’m customizing the sci environment.

👍 2
Sam Ritchie14:01:16

@U04V15CAJ yep, that’s today’s job: • getting this PR finalized • writing up how to use it • adding a template to clerk-utils that generates a project using it

Sam Ritchie14:01:11

@U098UL4QP I have been spending a bunch of time wrapping up React interfaces to make them play nice with ClojureScript, so please let me help once you get a project up!

👍 2
thanks3 2
Sam Ritchie14:01:54

@U04V15CAJ pretty awesome that we can point people at the sci configs project (https://github.com/babashka/sci.configs) and say “here are some ideas for what you might want to include in your Clerk environment”

🎉 4
jaydeesimon22:01:16

Also, I just wanted to express some gratitude for Clerk. It’s been a game changer for me in terms of understanding problems and communicating that understanding to other people. It’s such a great tool so I’ve been doubling down on my understanding of it. I look forward to its progress and would be happy to contribute in any way I can 🙌

🙌 6
🎉 4
🖤 6