Exploring React Native + Expo with shadow-cljs

Disclaimer: I know absolutely nothing about react-native or expo but I know a lot about optimizing/packaging JavaScript.

I was exploring options to make react-native development with shadow-cljs easier. I did not like the hoops I had to jump through to make the metro packager work reliably. It sort of works out of the box but it pretty slow and frequently gets in the way of the stuff we want to do in CLJS (eg. REPL or hot-reload).

I have this Expo app on my phone which lets me scan a QRCode which then opens the app by talking a linked HTTP server. It seemed pretty straightforward to just copy what this HTTP server did by default and write my own. So about 100 lines of code later I had a working prototype that could load my app directly from the Expo app without any of the react-native or expo tools running. It is not perfect and comes with all sorts of limitations but it might be something worth exploring.

Long story short it is now possible to use shadow-cljs to build an Expo app entirely without either expo or create-react-native-app running. You still need the npm packages installed though.

(ns demo.expo
  (:require
    ["react" :as react :rename {createElement $}]
    ["react-native" :as rn :refer (Text View)]
    [shadow.expo :as expo]
    ))

(defn render-root []
  ($ View nil
    ($ Text nil "Hello World from CLJS! 1")
    ($ Text nil "Hello World from CLJS! 2")
    ($ Text nil "Hello World from CLJS! 3")))

(defn ^:dev/after-load start []
  (expo/render-root render-root))

(defn init []
  (start))

Compared to other react-native things I have seen for CLJS this has virtually zero boilerplate besides the stuff generated by create-react-native-app. I did not use any react wrappers (eg. reagent) because I wanted the example to be as pure as possible. They all should just work though as long as they can generate a ReactElement.

At this point this is merely a proof-of-concept but with some more work it might be possible to turn this into something usable. It was kind of refreshing to get rid of the metro server completely since it was pretty frustrating to work with.

I have a bunch of questions if someone with in-depth Expo or react-native knowledge wants to help out.

20 Likes

This looks awesome, thanks. Nothing to add except thanks for the work on shadow-cljs, don’t know how I would have done Clojurescript without it. And it just keeps on getting better.

1 Like

This is really awesome! Are there any specific open questions/TODOs that others (like myself) could start hacking on?

1 Like

Probably too early to start hacking on anything particular.

The server part is not a whole lot of code and I’ll add the source-map support later today. One thing that seems really important that doesn’t currently work is the support for using Chrome to debug remotely. I could not find any documentation about either the protocol or the steps involved to make this work. AFAICT its just a websocket connection between the worker and the server to transfer messages around. It might be total overkill to try to do this via shadow-cljs or it might be really simple. I don’t know.

The build part is also not a whole lot of code and I think the only part that missing is source maps and release support so you can publish something. For that there is probably some exp interop required.

I hacked the server part by looking at Wireshark dumps of a client talking to the actual CRNA server. I replicated the bare minimum of requests but I still have no idea what half of them do (eg. /messages, /inspector/device, /onchange).

I’m curious if it is possible to do this with Expo at all. Can I start the simulator by just providing an URL (or File) which it should use? react-native interop for release builds should be possible via the --transformer option maybe. Might even work for watch but I kinda like not having metro running at all.

The default tools are all pretty heavily integrated and coupled to the metro bundler. I couldn’t figure out how to make it ignore any of it and just use a provided URL.

If you understand javascript maybe you can take a look at this:
https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome

It is the chrome debugger plugin for VisualStudioCode and it is open source.

That is not the Chrome debugger I was referring to. Expo (or RN, not sure) has this built-in “Remote Debug” option where it loads the actual code in the Browser in a WebWorker but still displays the app normally. So the JS doesn’t actually run in the device which means you can use the Chrome devtools directly.

Sorry for the misunderstanding

There’s some resources here: https://github.com/ChromeDevTools/awesome-chrome-devtools#using-devtools-frontend-with-other-targetsplatforms

Is it actually running it in a webworker, or implementing the Chrome DevTools protocol?

It is a Web Worker. Just try it with a standard create-react-native-app. I know nothing else about this except for what I can see and that is a the JS running in a Web Worker instead of the device. Maybe it uses the Chrome DevTools protocol I don’t know.

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.