2

I am trying to use the react-native-reanimated library in the clojurescript project. And for this library, I have to create worklets by adding the "worklet" directive at the top. ex:

function someWorklet(greeting) {
  'worklet';
  console.log("Hey I'm running on the UI thread");
}

How can I use same in the clojurescript, currently I have:

(defn someWorklet [greeting]
    (js/console.log "Hey I'm running on the UI thread"))    
hrca
  • 458
  • 3
  • 10
  • At the risk of stating the obvious, did you try `(defn someWorklet [greeting] "worklet" (js/console.log "Hey I'm running on the UI thread"))`? Does the compiler strip out the unused string literal? – Jared Smith Jun 04 '22 at 13:56
  • 1
    yes, adding "worklet" had no affect. – hrca Jun 04 '22 at 14:16
  • 1
    this is babel plugin which looks for "worklet" directive and converts that into a worklet function: https://github.com/software-mansion/react-native-reanimated/blob/main/plugin.js .Looks like this plugin is more tailored toward javascript syntax and not working in clojure – hrca Jun 04 '22 at 14:21
  • 1
    Yeah I think the underlying problem is the closure compiler. I don't think there's a way to tell it not to strip out pragmas. I hope somebody drops by and proves me wrong. – Jared Smith Jun 04 '22 at 14:52
  • https://www.npmjs.com/package/babel-plugin-assign-directive plugin can be used for "worklet" directive for cljs, but unfortunately this don't work with shadow-cljs (that's what my project uses) – hrca Jun 04 '22 at 18:42
  • 3
    One workaround can be to not rely on directives and the library can just provide a function to manually create worklets. https://github.com/software-mansion/react-native-reanimated/issues/1090#issuecomment-1146681621 – hrca Jun 04 '22 at 20:53

1 Answers1

3

My advice would be to keep those "worklets" in JavaScript.

As far as I know they run in their own context and therefore would need to load the entire ClojureScript runtime. At which point they likely lose all their performance benefit they may have had. Also I assume it is going to try to "copy" arguments passed to them. So you can't pass and CLJS datastructures (maps, sets, vectors, keywords, etc.) as arguments to them since that copying does not work for those. If you serialize them instead all potential performance benefit will be gone again.

The main hurdle isn't the "worklet" directive, you could hack those in yourself via (js* "\"worklet;\"") as the first "call" in a defn. Closure optimizations will likely remove them though.

The main hurdle is that those directives need to be processed by the react-native build tools and it likely won't understand the ClojureScript code in the first place.

So instead write them in JS and include them in the CLJS builds in a way that the react-native tools can find them. So this could be (def my-worklets (js/require "../my-worklets.js") using a valid path here. In case of shadow-cljs this path would need to be relative to the :output-dir. Same for krell I believe.

Thomas Heller
  • 3,842
  • 1
  • 9
  • 9
  • Even after using the js file for creating the "worklet", it didn't work. App crashed both times (with or without "worklet" directive) with the below error. (Attaching separately, long comment). Also, for testing purposes, I created a simple react-native project, and with "directive" it just worked but without it crashed with the exact same error. It means in `shadow-cljs` project adding directives does not working even in the js file. Maybe the babel plugin is not only affected by file type but also by project type/configuration. wdyt? – hrca Jun 07 '22 at 23:20
  • "Abort message: 'JNI DETECTED ERROR IN APPLICATION: JNI GetObjectRefType called with pending exception java.lang.RuntimeException: Tried to synchronously call function {hello} from a different thread." More details: https://github.com/status-im/status-react/issues/13452 – hrca Jun 07 '22 at 23:21
  • Do you have your simple project available somewhere? Maybe you didn't move "enough" of the worklet over? I have never used any of this so I'm only guessing. Since it is part of a babel plugin that runs during compilation all code related to the worklet needs to be plain JS and it cannot take any CLJS arguments in scope at all. – Thomas Heller Jun 08 '22 at 06:52
  • These are "cljs" and "js" projects where I am using the same file `box.js` for reanimated `worklet`. cljs(only last commit): https://github.com/Parveshdhull/status-react/tree/feature/reanimated2 js: https://github.com/Parveshdhull/reanimated-js also crash error: https://github.com/status-im/status-react/issues/13452#issue-1261207494 Thank you for looking into this. – hrca Jun 08 '22 at 23:38
  • 1
    I specifically mentioned to use `js/require` to include the JS file. Using ns `:require` does not work since that is processed by shadow-cljs and not metro. Using the `js/require` will make it go through metro first which is what you need here. – Thomas Heller Jun 08 '22 at 23:42
  • Sorry, my bad. Using `js/require` fixed all issues, working great now. Thank you very much. – hrca Jun 09 '22 at 00:08