Skip to content
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

React native hot reloading limitation #544

Closed
prestancedesign opened this issue Aug 15, 2019 · 11 comments
Closed

React native hot reloading limitation #544

prestancedesign opened this issue Aug 15, 2019 · 11 comments

Comments

@prestancedesign
Copy link
Contributor

prestancedesign commented Aug 15, 2019

I try to figure out how to handle correctly (like Figwheel does) the hot reloading with Shadow-cljs for React native projects.
If I'm correct, this method handle the reloading :

(defn render-root [root]

That works great until the modified file is not the root app.cljs (for eg.) or some another files directly required by this app.cljs.

For better understanding, you can check and test with here prestancedesign/reagent-expo@da58d8f.

@Wiltzu has mentionned this behaviour on this issue #214 :

I found another issue that is related to reloading JS files with watch. Basically, I have core namespace, that loads views from screens and it uses multiple custom_views from different namespaces (core -> screens -> custom_views). So, when I changed custom_views only screens gets loaded, but not core. It results views not been re-rendered. Is it so that only the closest dependencies are reloaded? Is there some configuration to change it or what should I do?

For the moment, the only solution we had to avoid this behaviour is to use (ns ^:dev/always app.core) to force app.cljs to always be recompiled/reloaded.

Any ideas how Figwheel handle this and how can implemented this for Shadow-cljs ?

Best regards.
Thx.

@thheller
Copy link
Owner

thheller commented Aug 15, 2019

Technically the issue is in reagent/react.

The code is reloaded normally, then the start fn is called to trigger a re-render. That renders root and then Reagent decides to skip further rendering because root itself didn't change.

In figwheel a full :recompile-dependents is triggered which means that is compiles and reloads a whole lot more files with each cycle, resuling in a re-definition of root thus changing it. That "hides" issues like these but also makes things much slower in bigger apps.

One simple fix for this is to force reagent to re-render by adding an additional prop.

(defn start
  {:dev/after-load true}
  []
  (expo/render-root (r/as-element [root {:x (js/Date.now)}])))

@prestancedesign
Copy link
Contributor Author

Hi @thheller and thanks for all this valuable informations !

Thx to you to pointing me in the right direction to find the figwheel doc :

:recompile-dependents

For correctness the ClojureScript compiler now always recompiles
dependent namespaces when a parent namespace changes. This prevents
corrupted builds and swallowed warnings. However this can impact
compile times depending on the structure of the application. This
option defaults to true.

Perfect, the trick to adding the variable prop does the job.
Is it better to use this fix instead to use ns ^:dev:always test.app because of performance, right ?
This allow to re-rendering without reload root if not necessary (not changes), isn't it ?

@thheller
Copy link
Owner

^:dev/always will be slower yes so it is better to use the props "trick".

@prestancedesign
Copy link
Contributor Author

prestancedesign commented Aug 15, 2019

If the answer is more complex than that, I will open an new issue but I pose my last question here (for today ;)).

Why when reloading app (hard reloading), we loosing the repl connection (and/or websocket) ?
Because sometimes a runtime errors occur, I click dismiss but the app displaying a blank page.

With figwheel, if I hard reload the app, the app state is loosing but the repl connection is restored and I can continue to working.
How can we fix that with Shadow-cljs ?

@prestancedesign
Copy link
Contributor Author

After "hard" reloading the app, the connection expo message is displaying well:

WebSocket connected!
REPL init successful

But if I launch something from CLJS repl, timeout error occurs:

[1:1]~cljs.user=> (js/alert "ok")
Timeout while waiting for REPL result.

@thheller
Copy link
Owner

Hmm it appears that the websocket is not disconnected when doing a hard reload. It just kinda stops replying I guess. Not sure why that is. Will look into that.

@prestancedesign
Copy link
Contributor Author

Not sure why that is. Will look into that.

Thx very much !

N.B. It's seems that is the same issue mentioned to the third point by @Wiltzu: #214 (comment)

When developing, Android app is generating a new REPL runtime every time the app is reloaded and previous runtimes are left running, but are not active. Reloads with iOS are working fine and only one runtime is active all the time. Is there any way to close those runtimes or select runtime in IDE like Cursive? (shadow/repl-next :app) is only working in command line.

@thheller
Copy link
Owner

If it works find in iOS I'm gonna say its a bug in react-native. A quick google shows many other websocket closing relating issues, eg. facebook/react-native#18775

I'm not sure there is much I can do on my side. Since the code of the original app stops running I can't do anything there. I also don't want to forcefully kill other runtimes if a new one connects since that would prevent you from connected with 2 devices at the same time.

I would expect the react-native runtime to close all open websockets when the app is unloaded. Maybe there is an event I'm supposed to listen for but I couldn't find it.

@prestancedesign
Copy link
Contributor Author

If it works find in iOS I'm gonna say its a bug in react-native.

I don't tried on IOS but for me, it works well for the same project with Figwheel...as I say before, I can reload without loosing the REPL connection so I don't think, it's a bug in react-native.
Am I wrong ?

Best regards,
thx for your research.

@thheller
Copy link
Owner

It is definitely a bug in react-native. The websocket remains open. That I confirmed.

Figwheel might be using a different logic for runtime selection. In shadow-cljs the first JS runtime that connects is picked and used for eval's. You can pick a specific runtime but that API isn't exposed since no tool currently supports it properly. Since that first JS runtime (ie. websocket) never disappears it never picks a new runtime.

Figwheel just might pick the latest? Or just send to everything? I don't know.

I could work around react-native not disconnecting but I kinda don't like that since the websocket should just be closing.

Dunno, too tired to look into it now.

@prestancedesign
Copy link
Contributor Author

OK thank you to help me had a better understanding of all of that !
I do Clojurescript less than 6 month and I'm very curious and motivated haha :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants