This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2021-10-20
Channels
- # announcements (33)
- # aws (1)
- # babashka (8)
- # beginners (100)
- # calva (59)
- # clara (4)
- # clj-kondo (33)
- # cljdoc (9)
- # cljs-dev (30)
- # cljsrn (1)
- # clojure (24)
- # clojure-australia (1)
- # clojure-boston (1)
- # clojure-dev (4)
- # clojure-europe (14)
- # clojure-france (5)
- # clojure-italy (7)
- # clojure-nl (1)
- # clojure-uk (36)
- # clojurescript (13)
- # clojureverse-ops (6)
- # conjure (2)
- # cursive (2)
- # datahike (11)
- # datalevin (1)
- # datomic (106)
- # graphql (3)
- # helix (10)
- # holy-lambda (24)
- # kaocha (2)
- # lambdaisland (3)
- # lsp (199)
- # malli (35)
- # off-topic (16)
- # pathom (7)
- # polylith (38)
- # portal (16)
- # quil (2)
- # re-frame (18)
- # reagent (57)
- # shadow-cljs (11)
- # testing (3)
- # xtdb (9)
I'm trying to translate https://robkendal.co.uk/blog/2020-02-20-creating-a-react-code-editor-and-syntax-highlighter over to a CLJS project, but I'm having trouble with writing a nice Reagent version of:
useEffect(() => {
Prism.highlightAll();
}, [props.language, content]);
I have a (def content (r/atom ""))
, but there doesn't seem to be a way of just calling Prism.highlightAll()
whenever content
is updated - r/track
doesn't seem like it'll do exactly what I want, since I'd need to actually include @content
somewhere in the function body for r/track
, but Prism.highlightAll()
doesn't depend on @content
at all
Also, how would I translate this:
import Prism from "prismjs";
Prism.highlightAll();
To answer your last question, something around the lines (ns app (:require ["prismjs$default" :as prism])) (.highlightAll prism)
Got an Uncaught TypeError: Cannot read properties of undefined (reading 'highlightAll')
when I did (.highlightAll prism)
Also couldn't I use something like (:require ["prismjs" :default prism])
in shadow-cljs
?
Yes, that's exactly what you'd use in shadow-cljs. There's a whole section on using NPM packages in the documentation: https://shadow-cljs.github.io/docs/UsersGuide.html#_using_npm_packages
Yeah, it throws the same error of Uncaught TypeError: Cannot read properties of undefined (reading 'highlightAll')
when I use :default
instead 😞
Just read that section, it has an answer. :default
is not always the solution - depends on the package.
There's a table with JS->CLJS imports translation. But it can be used in only about ~80% of the cases (just eyeballed the number based on my own experience). The rest is described by the text within that section.
FWIW consider :default
deprecated. It was a non-standard addition and $default
is the now standard way which shadow-cljs also support, so thats what you should be using if anything. I'll update the docs accordingly soon hopefully.
@U2FRKM4TW I used ["prismjs" :as prism]
since the "prismjs" package seems to be able to be require()
d normally as well, but (.highlightAll prism)
now seems to not do anything at all
This is a gist for reference: https://gist.github.com/hanyuone/c919df5ac53260ae9ea4275499a42b36
and very much how you use it. you are using a react based rendering but prismjs doesn't work that way
@U05224H0W is there a way of copying this functionality from the tutorial I was following into cljs?
useEffect(() => {
Prism.highlightAll();
}, [props.language, content]);
yes, exactly like that. I mean translate it from JS 1:1 and then lookup how to use hooks in reagent
So I'd have to use react/useState
and react/useEffect
in my Reagent component too instead of messing around with the r/atom
?
@U013HB9EFT8 You don’t need to use React hooks to get this to work. (1) from your console log of prism
, it shows that highlightAll
is a field on the prism
object (which you got from the require alias ["prismjs" :as prism]
, with a function value that takes no arguments. So, you call it simply like this in CLJS: (prism/highlightAll)
. (2) Since Shadow-CLJS does not load CSS dynamically into your src
(at least, not by default), you will need to copy the Prism CSS file from node_modules
into your resources/public CSS file. (3) I have found for the highlightAll
, you will need to push it onto the event callback queue so that it runs after the DOM is updated, which you can do using a setTimeout
(also described in https://betterstack.dev/blog/code-highlighting-in-react-using-prismjs/)
I’ve made a minimal example of Prism using Shadow-CLJS and Reagent:
@U05224H0W Ah, what should I use instead?
the correct solution is either hooks or the usual old component lifecycle hooks such as :component-did-mount
or :component-did-update
I’ve tried using ;component-did-mount
without the setTimeout
, and it didn’t work…
never ever use setTimeout to trigger a side-effect of rendering since there is absolutely no guarantee it'll run when you intended it to
you can also just use a ref. I'd assume that prism has a highlight(domNode)
in addition to highlightAll
OK, I will try to rewrite using refs…
Yes, I believe it has @U05224H0W
Thanks for the warning about setTimeout
:thumbsup:
react gives you ways to "hook" into the lifecycle at the point when you need it to. use them. do not ignore the lifecycle and just fire off async events in the hopes that they trigger after the lifecycle is actually done
especially with react concurrent mode and things like suspense there is no guarantee that a setTimeout
(or any other) doesn't run before something is actually rendered
there is always a "you are guaranteed this node is now mounted in the document" point and callback. that is the time you want to do stuff.
everything else is just a hack, which might work but has no guarantee to do so and may break randomly for users with slower devices or slower internet connections that you use when testing
> before something is actually rendered Sounds like using such functions should still be fine when the delayed functionality does not rely on having anything rendered, right? E.g. changing the state of a component, like in this contrived example:
(defn view []
(let [x (reagent/atom 0)]
(fn []
(js/setTimeout #(reset! x 1) 0)
[:span @x])))
Just to be clear - I'm not trying to justify anything, just trying to understand the boundaries.That is still wrong. Reagent won't run the inner function the third time. It runs only when the properties are changed.
still a really bad way to do this given that you render one thing and then immediately another