-
-
Notifications
You must be signed in to change notification settings - Fork 181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support target for React Native #214
Comments
I only played with
Need to specify one entry/main namespace that should do the AppRegistry call. The REPL client and hot reloader could be injected before into that. We can probably get away without the Source maps will probably not work until metro supports it, see facebook/metro#104. Could maybe try to bypass metro completely and live in our own world instead. I think |
|
@DogLooksGood source maps work with figwheel since the files are not processed by the react-native packager. no idea what that specific PR did but it definitely didn't "enable" anything.
|
Hi there,
I'm generally pleased with how nicely everything works together. In particular, shadow-cljs allows one to be minimally invasive, and just change things as one needs. Also, react-native's own hot reload is perfectly sufficient for smooth development. However, there are a few pain points:
From my point of view there are two things a react-native target could do to make things nice:
Having an Anyway, those are my 2c. |
The problem with
In the meantime I could probably write a simplified REPL client that is not the browser client. But I have no idea what networking capabilities are available in react-native? Can I use WebSocket? Can I use npm |
Thanks again for your quick response! I was aware Anyway, react native has websockets – https://facebook.github.io/react-native/docs/network.html#websocket-support and most other things you have in the browser (except the dom, of course). What it doesn't have is all the stuff one might have in node (fs, net, ...). One can always use the bridge to access native capabilities, but generally it's a pain best avoided. A rule of thumb might be: if you can do something in a webworker, you can probably do it in react native. I actually quite like the idea of the original poster: defining a From my side: please don't spend too much time on this unless you wish to. Hot reload means one can build the UI components fairly ergonomically, and for everything else, one can just compile to a non-react-native target and enjoy the repl there. |
React Native hot reloader will work without figwheel_bridge.js. The client for browser not work because it uses dom. And the reason why source map not work may relative to the path, not sure for this. but the command |
I did some basic work trying to create a target for You can either use the
The goal was to create a dedicated
Since this has a single entry the REPL code is automatically added. You I kinda got frustrated by Anyways I'll probably look into this more once the frustration wears off. |
Hi there, thanks for keeping looking at this! I completely sympathise with your frustration – I constantly feel like I fight similar things when using react native, especially with clojurescript : a build tool on top of a framework on top of a build tool on top of a build tool. What could go wrong?! That's actually one of the main reasons I first looked at shadow-cljs: it was appealing to be able cut out some of the stuff hidden behind the shadows, and at least just build the clojurescript directly. As far as I'm concerned, it would be super if you built to two files: one which contained the compiled and concatenated source, and another with the repl. It's really no annoyance to add a dev index.js to require both, and that way one can choose not require the repl when one doesn't need it. Bundling all the non-repl code into one file is nice, though, because the metro bundler is slow, and the standard npm-module output leads to a very slow initial metro bundle when one first runs react-native... Indeed, I've been spending the last couple of days trying to figure out a decent workflow with shadow-cljs, and it seems actually to be pretty nice to have two targets: one building to react-native and running in the emulator, another building to node and shimming out the react-native deps, and doing most of the development there. The constant react-native rebuild and rebundle is annoying, and if you're running emulators you get your own frustrations: it turns out much quicker to do much of the work in a "headless" environment, and only when you actually do ui work, fire up the full react-native machine. I actually came across a very weird issue today (I'm almost certain it's not shadow's fault, but I might as well mention it, in case you think it is): when requiring react-native inside of clojurescript, everything works fine, except the "InputAccessoryView" disappears (in other words, Anyway, I'll have a look at what you've done when I next sit down to code. Thanks again: I love shadow. |
By the way, does shadow-cljs understand |
Not sure why Emitting just a single file for development was actually the first thing I tried. It is very easy to do BUT the It seems to me that the best option really is to bypass the |
Ah, ok. In that case lots of small files :) At least one then only has one slow build (on my machine, about four minutes), but after that the incremental builds are fast. |
Quick note: If you add a .babelrc to your |
Ah, thanks for the tip! |
After I got completely frustrated with the expo and the react-native packager (again) I started investigating an alternative. The idea was to run This works perfectly for my basic test app. REPL and live-reload both work as expected and flawlessy. Since the react-native packager or expo tools don't run at all they can't interfere with anything either. You can still require any npm package and those will be bundled by the Just need to add source map support and some HTTP methods the expo/rn app seems to call (eg. My app is pretty limited though so it might totally blow up when you do actual stuff. (ns demo.rn
(:require
["expo" :as expo]
["react" :as react :rename {createElement $}]
["react-native" :as rn :refer (Text View)]
["create-react-class" :as crc]))
(js/console.log "foo10")
(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")))
(defonce root-ref (atom nil))
(defn ^:dev/after-load re-render []
(.forceUpdate @root-ref))
(defn init []
(js/console.log "rn-init called")
(let [Root
(crc
#js {:componentDidMount
(fn []
(this-as this
(reset! root-ref this)))
:render #(render-root)})]
(expo/registerRootComponent Root))) Config currently looks like this:
Basically I point the Expo app at Since I know nothing about either react-native or Expo I hacked something together with Is there perhaps an Expo CLJS demo app somewhere I could use for testing? I'll publish what I have now later after cleaning up some hard coded IPs and such. |
I'm very excited on this! I tried to create an shadow-cljs + rn example using Such a pity that I didn't do too much rn development for a very long time (since lymchat), so I can't I'm bootstrapping a new project now, the web app was using shadow-cljs since the start, and soon I can work on Expo with shadow-cljs. Looking forward to it, really cool! |
If use the offline bundle, is that we have to create a new bundle whenever we change our npm dependencies? |
Still need to figure out if its possible to do the Remote Debugging thing in Chrome somehow via shadow-cljs too. Seems pretty important but so far that seems to be the only thing missing. Removed some more boilerplate code. This looks reasonable now. (ns demo.rn
(: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)) |
Alright the basics are working and I made a sample project. https://github.com/shadow-cljs/expo-test Only Anyways please try this and report if it actually works. Only tested this on my PC so it might not work at all anywhere else. |
That looks pretty awesome! Nice way to turn the build process around. I wonder if one can do something similar with "vanilla" react-native (ie, without expo)? From my very basic understanding of how react works under the hood, it ought to, but I wouldn't know how to make it do so. It would be nice if it could: in my projects, I need some native modules, so expo is out of the question. I think, though, that finding a solution that works well with create-react-app and expo is an excellent target: expo eases a lot of pain in the app development process, and create-react-app is, by now, the standard way to start a new project. If the solution that comes out also works with the vanilla react-native-cli, all the better. |
Same here. In theory I see no reason why it wouldn't work. Just need a way to run bundle the iOS/Android app with the JS we created instead of what the The main problem seems to be that all the |
Yes: I slapped together a node webserver this morning, and tried to figure out what a react-native dev app requests, but it wasn't as simple as I had hoped. If I have a spare afternoon, I'll burrow into the react-native code, and see if I can reverse engineer what's going on. To be honest, even if I figure it out, I hate doing this sort of reverse-engineering. One is very dependent on api that may change at any time. Even if you get everything working, you just need a version bump, and you're back to square one... In the mean time, I'll do most of my cljs dev work building to node, and only build for react-native when I need to. |
My thinking was that the RN app will just load some JS and we just need to replace which one it loads. I thought the react-native app would just bundle the JS + assets somehow and don't talk to a remote server at all but that might be completely wrong. I share your concern and I definitely do not want to do this work for every other react-native/expo release. The It might still be best to go with the figwheel-bridge.js way of completely loading the CLJS code separately. |
Yes: in theory, it should be a simple case of telling the react-native app to load a different piece of js + assets. I think it might even work when building for "release" -- one could just swap the bundle. What I meant with the server was when running react-native in "dev": it then starts a web-server that serves up the js to be used, and any diffs each time the code changes. Ideally one could just swap out where that server serves its assets from, but, as happens with all projects that have had some time, by now there's a lot of extra stuff that server does, and with just a short time playing around, I couldn't figure out quite how to do so. I think the figwheel-bridge way of doing things is perfectly valid. My only problem with it is that it puts yet another tool between your code and what's actually run. That means that sometimes you get figwheel handling errors, sometimes you get the react-native bridge handling errors, if you mess with native code, sometimes there are errors there. Keeping abreast of what is actually a problem, and what is just a build problem, can become quite complicated (at least for me). |
I see there is an "expo" target now. Is there any documentation on using this? https://github.com/thheller/shadow-cljs/blob/master/src/main/shadow/build/targets/expo.clj |
@AndrewMorsillo no that target was an experiment and is broken. Do not use it. I don't use I'll try to write some docs to show the basic setup. |
@thheller ok thanks. The reason I ask is that I am interested in learning cljs by prototyping a react native app. I used the expo Leiningen template which works but isn't configured for connecting to development tools nREPL (Calva in vscode in my case). I've had a difficult time figuring out how to configure Leiningen/figwheel to start a cljs nRepl with cider. In fact I still haven't figured it out. Shadow-cljs seems much easier to use than lein so I was hoping to find a configuration/template for use with expo. |
@AndrewMorsillo I created a demo app for fulcro+expo here: https://github.com/thheller/fulcro-expo I'll probably create an example app for |
Thank you very much for creating that. That will be a great place for me to start exploring. |
Finished the reagent demo as well. https://github.com/thheller/reagent-expo I'll try to write proper docs this weekend. |
I am extremely appreciative of the work you're doing in this direction. I have a fairly mature, non-expo Re-Frame React Native app built with re-natal that I'd love to migrate to shadow-cljs for so many reasons, but I'm a bit wary of heading down that road as I can tell the level of tooling knowledge is well over my head. Do you have plans, or a recommended approach for getting these demos running in an "ejected" state? |
@attentive "ejected" doesn't change anything regarding the |
I'm working on a React Native template with Shadow CLJS, that could replace re-renal in our projects. I have managed to change
|
@Wiltzu can you elaborate on the " a new REPL runtime every time the app is reloaded" part? You are supposed to completely disable all built-in reload functionality from react-native/metro. It should not be reloading anything ever. shadow-cljs does its own reloading and would just cause issues if something else was reloading as well. Picking a REPL session is tricky since the
|
@Wiltzu I was able to get cljs-devtools working by disabling the advanced-build check. In my app entry point, I manually load the cljs-devoots: (devtools/set-pref! :disable-advanced-mode-check true)
(devtools/install!) (I'm not using the |
@thheller Thank you for quick response! I have live and hot reload disabled from React Native. I meant manual reloading, that is usually required, when code is broken. After that I could no longer use nREPL, because the first runtime is no longer active, but it still gets selected. All the requests will timeout after that. Could it be possible to delete runtimes manually or with some condition, when those are not usable anymore? Switching runtimes would also be nice, when developing both Android and iOS apps at the same time. @svdo thanks that seems to be a good workaround! This could be then included in projects own preload file. I would also like to share this. It can be used as a (ns shadow.react-native
(:require
["react-native" :as rn]
["create-react-class" :as crc]))
(defonce root-ref (atom nil))
(defonce root-component-ref (atom nil))
(defn render-root [app-key root]
(let [first-call? (nil? @root-ref)]
(reset! root-ref root)
(if-not first-call?
(when-let [root @root-component-ref]
(.forceUpdate ^js root))
(let [Root
(crc
#js {:componentDidMount
(fn []
(this-as this
(reset! root-component-ref this)))
:componentWillUnmount
(fn []
(reset! root-component-ref nil))
:render
(fn []
(let [body @root-ref]
(if (fn? body)
(body)
body)))})]
(.registerComponent rn/AppRegistry app-key (fn [] Root)))))) It can be used like this |
@thheller I found an other issue with the websocket that is used to reload codes. It selects IP with some logic and if I have a iOS device connected with an USB cable, it starts using |
@Wiltzu yeah the IP detection unfortunately is a bit unreliable. You can configure it manually by setting
I'm sort of hesitant to include the |
@thheller thanks! Nice to know that changing IP is possible, when it's required. I found another issue that is related to reloading JS files with watch. Basically, I have |
Why does If you want you can set |
@theller the problem wasn't the thing that
|
Just a "FYI": when using the |
@Wiltzu I'm attempting to get a project working without expo and I can't seem to get past the |
@Wiltzu Never mind, I seem to have fixed it. I'm not entirely sure how I did it though... |
Interresting, I have exactly the same issues as @Wiltzu. When reloading manually mobile app, the repl return timeout because the websocket or something changes. Do you have find solutions @Wiltzu ? |
I think support is good nowadays. Please open separate issues when there are still remaining problems. |
Hi, @thheller. Thanks for all your hard work on this wonderful project!
I test the usability with following setup. I can have
HMR
andREPL
, source map is not support yet.https://github.com/DogLooksGood/shadow-cljs-with-react-native-note
I think it will be awesome to have a
react-native
(orjavascript-core
?) target.The goal should be use without shim.
List the important steps here:
:devtools
tohttp://host-ip:9630
shadow.cljs.devtools.client.browser
, in this client, don't usejs/document
.The text was updated successfully, but these errors were encountered: