Fork me on GitHub
#cljsrn
<
2021-05-24
>
Michael Jung11:05:30

I’m trying to use react navigation 5 in my Krell RN app, however, I’m having difficulties loading it in my cljs files. On their “Getting started” page, they say that we have to use:

import { NavigationContainer } from '@react-navigation/native';
I’ve tried to replicate this as follows in cljs:
(ns my-app.core (:require ["@react-navigation/native" :refer [NavigationContainer]]))
But NavigationContainer just ends up being undefined/nil. If I run
(require '["@react-navigation/native" :refer [NavigationContainer]])
from the REPL, I will get
Unexpected error (IllegalArgumentException) compiling at (REPL:1).
Don't know how to create ISeq from: java.lang.Character
I’ve also tried the following:
(js/require "@react-navigation/native")
But this also returns nil. Any ideas what I am doing wrong here? For the installation of react navigation, I have followed their instructions:
npm install @react-navigation/native
npm install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

dnolen12:05:11

@michael.w.jung did you check if you need to import $default

dnolen12:05:23

there's no magic way us to detect that you need to look at the library and see how it's exported

Michael Jung15:05:12

@dnolen You mean like this?

(ns my-app.core (:require ["@react-navigation/native$default" :refer [NavigationContainer]]))
This doesn’t work either. Also, as we import it with curly braces in JS, it shouldn’t be a default export, right? I don’t know much about these things, though...

dnolen15:05:25

@michael.w.jung you need to know about 🙂

dotemacs15:05:41

I’m using this library, with shadow-cljs, and it works. I’m not saying that it’s a krell/shadow-cljs thing. It’s probably something to do with your setup… If you want to see what is available when you require a given library, require it in the REPL:

(require "@your-lib")
If you don’t, then something went wrong upstream.

dnolen15:05:53

examine the the library, see if exports default and something else

dnolen15:05:09

@michael.w.jung also (js/require "@react-navigation/native") if that doesn't work as you said above then something else is probably wrong

dnolen15:05:58

oh sorry - but you can't do that anyway

dnolen15:05:11

React Native is not Node.js you have to understand this

dnolen15:05:26

if Metro can't see it - you cannot require it

dnolen15:05:38

Metro is never going to see your ClojureScript files - so that will never work

dnolen15:05:39

To avoid issues like you have to understand what Krell must do to satisfy Metro

dnolen15:05:21

1. Compiling your project will detect Node modules - this gets dumped into a file that Metro does see in the output directory

dnolen15:05:42

2. If your Node import isn't in that file - it's never going to work

dnolen15:05:29

look at output-dir/npm_deps.js

Michael Jung15:05:52

It seems to be there:

module.exports = {                                                                                                                                                                                                                                                              
  npmDeps: {                                                                                                                                                                                                                                                                    
    "react-native": require('react-native'),                                                                                                                                                                                                                                    
    "react-dom": require('react-dom'),                                                                                                                                                                                                                                          
    "@react-navigation/native": require('@react-navigation/native'),                                                                                                                                                                                                            
    "react": require('react')  }                                                                                                                                                                                                                                                
}; 

dnolen15:05:13

if it's there then

dnolen15:05:18

A) it was in node_modules ... good

dnolen15:05:25

B) Krell saw it ... good

dnolen15:05:37

so you're running out of possibilities of what could be wrong

thheller15:05:35

did you reload your RN app? new npm dependencies cannot be hot-reloaded or REPL require'd in without being there by the initial load first

dnolen15:05:29

right there's absolutely nothing dynamic about Metro

dnolen15:05:49

you have recompile ClojureScript & reload the entire app if you import something from node_modules

dnolen15:05:59

(for the first time)

Michael Jung15:05:19

node_modules/@react-navigation/native/lib/module/index.js looks like this:

export * from '@react-navigation/core';                                                                                                                                                                                                                                         
export { default as NavigationContainer } from './NavigationContainer';                                                                                                                                                                                                         
...
So no default export if I understand this correctly

Michael Jung15:05:47

Yes, I think I reloaded / rebuilt everything.

Michael Jung15:05:20

I threw away the whole target directory and reran clj -M -m krell.main -co build.edn -c

dnolen15:05:51

another way to debug is the edit index.js and console.log this library that you're trying to use

dnolen15:05:08

there's nothing especially magical about the way Krell works

dnolen15:05:25

whenever you're done debugging you can always restore index.js by running a compile

Michael Jung15:05:42

Ok, I added the following to index.js

import { NavigationContainer } from '@react-navigation/native';                                                                                                                                                                                                                 
console.log(NavigationContainer);
console.log(typeof(NavigationContainer));
console.log(NavigationContainer === undefined);
The metro output says:
{"$$typeof": Symbol(react.forward_ref), "render": [Function NavigationContainer]}
object
false
So to me this looks like it works in JS

dnolen15:05:13

ok - so now fix up your :require to the way you originally had it

dnolen15:05:39

now if you look at the generated JS for that namespace you should see something that looks sensible

dnolen15:05:03

the one magic Krell trick here is this

dnolen15:05:59

there is no such thing as dynamic require in Metro - so we just monkey patch a global JS require function that will return the library then you invoke it

dnolen15:05:34

this is how you can get a handle on modules and assets even though Metro never saw ClojureScript

dnolen15:05:05

@michael.w.jung before you go look at though

dnolen15:05:37

console logging the npm_deps.js import in index.js is probably more useful - it's a JS object that will be used by ClojureScript to "import" those libraries

dnolen16:05:38

if somehow the library is null in there - then of course it can't work

Michael Jung16:05:13

Very odd. Now the library is no longer in npm_deps.js...

dnolen16:05:54

so this would be source of the bug if there is one

dnolen16:05:04

if it disappears from there - of course it won't work

dnolen16:05:30

but npm_deps.js will only get rewritten if you somehow run another compile

Michael Jung16:05:44

I deleted the target folder and reran clj -M -m krell.main -co build.edn -c. Still gone.

dnolen16:05:11

here's the one other thing

dnolen16:05:40

your ClojureScript require cannot be in some random file outside of the entry point dep graph

dnolen16:05:02

i.e. a.core is the app entrypoint and you have some side file b.core which imports Node library

dnolen16:05:10

which is never required by a.core

Michael Jung16:05:28

Ok, that’s why it was gone. I commented out some stuff before and it no longer was in that dependency graph. It is back now that I fixed this.

dnolen16:05:44

you are now a Krell expert 🙂

dnolen16:05:52

there's no other rules than the above

Michael Jung16:05:50

Well, I certainly don’t feel like one 😄

Michael Jung16:05:56

It seems to work now

Michael Jung16:05:46

I have no idea what I changed. It probably really was because I removed the require of the file that imported the lib when I tried to debug the issue.

Michael Jung16:05:19

Thank you very much for your help @dnolen. I was about to give up on this. Much appreciated!

dnolen16:05:10

No problem

dnolen16:05:50

But hopefully the above shows Krell works in a way intentionally to make debugging issues relatively straightforward

dnolen16:05:21

And I haven’t seen a library that can’t be used

Michael Jung16:05:22

Yes, it definitly did. I specifically chose Krell because it seemed rather simple and straightforward. I did some experiments with re-natal and expo in the past, but that was a complete mystery to me. I’m still pretty much a newbie in both CLJS and JS.

Michael Jung19:05:52

I have added another library to my project. This time with a default export. And again, I was having quite some trouble until I finally managed to get it to run. I think I’m still missing a piece of the picture: When I add a new dependency, which steps do I need to take? I thought I have to do this: 1. npm add my-lib 2. Require the library in cljs 3. Recompile using clj -M -m krell.main -co build.edn -c 4. Restart the Android app But I think this wasn’t enough. I also did the following things in some order I don’t remember: • Delete and rebuild the target dir • Restart metro with npx react-native start • Reinstall the Android app with npx react-native run-android (I’m not building for iOS at the moment)

dnolen19:05:50

Metro also has caching problems - but otherwise I don't think any of those other steps should be necessary

dnolen19:05:30

definitely have gotten into funny situations w/ Metro which have nothing to do w/ Krell that requires a whole cycle

dnolen19:05:55

whatever is on disk is the truth - and if it's still not working, I think the likelihood the problem is in Krell is quite low

dnolen19:05:19

but happy to take a specific set of steps to reproduce if you can come up w/ them - I dev on Android less so maybe it's spottier there