This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-06-27
Channels
- # babashka (45)
- # beginners (44)
- # calva (3)
- # cider (14)
- # clara (4)
- # clj-commons (3)
- # clj-otel (4)
- # cljsjs (1)
- # cljsrn (111)
- # clojars (5)
- # clojure (62)
- # clojure-europe (14)
- # clojure-nl (2)
- # clojure-uk (4)
- # clojurescript (31)
- # community-development (16)
- # conjure (7)
- # cursive (9)
- # data-science (1)
- # datalevin (10)
- # docker (1)
- # emacs (20)
- # fulcro (7)
- # helix (10)
- # jobs (4)
- # lsp (22)
- # malli (35)
- # meander (12)
- # music (1)
- # nbb (2)
- # off-topic (5)
- # pathom (3)
- # quil (1)
- # re-frame (12)
- # react (6)
- # reagent (18)
- # releases (1)
- # remote-jobs (1)
- # rewrite-clj (4)
- # ring (1)
- # shadow-cljs (10)
- # spacemacs (9)
- # tools-build (17)
hi folks. scratching my head a little on how I can dynamically update an element. am using expo & shadow-cljs & re-frame. example, I have a screen, which I can navigate to:
(defn app-configuration [props]
(r/as-element
[:> rn/SafeAreaView {:style (tw "flex flex-1")}
[:> rn/StatusBar {:visibility "hidden"}]
[:> rn/ScrollView
[:> rn/View
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]]]]))
when I edit TextInput's value, I can check the result is indeed updated correctly at the REPL by executing (<sub [:text]), however the value of the rn/Text label is not dynamically updated to match the subscription, only visible on re-visiting the screen. (i.e., seems (<sub [:text]) is only executed the once, and not dynamically updated. <sub is just (def <sub (comp deref subscribe))
am I missing something?I guess you're using something like React Navigation.
I've had the same behavior a lot of times, it appears to be that the navigators have some kind of cache for the mounted components in order to increase the performance (I guess).
What I do:
I usually have a namespace where I define my navigator, so I set the instruction for shadow to reload it always I make a change, also, I persits the navigation state (through a defonce
atom at first, then using the device's local storage).
I read again and realized your problem is different.
Why are you using (r/as-element ,,,)
at the top of your function?
If you can share more code it could help
If you're using this component just as a regular view, I think you shouldn't be using r/as-element
.
Or are you trying to pass this fn as a parameter to other component?
bear in mind, I still don't really know what I'm doing fully, am very new to this, first real experience with react or react native. you can see here that i define the fn as a component for the screen: https://github.com/dspearson/cljsrn-test/blob/master/src/app/index.cljs#L140
https://github.com/dspearson/cljsrn-test/blob/master/src/app/views.cljs#L28 is the view that i navigate to, which has the dispatch and event subscription that doesn't get updated live in the app, only on re-navigation
if I'm doing something horribly wrong, fair enough, I admit I don't really fully understand how all the bits fit together. have been trying to find working PoC examples to base my stuff off of, or looking at larger applicaations like status-react, but what i have right now is as barebones as i can imagine, i.e. a main screen, a way to navigate, and trying to live update & fetch.
I'm not familiar with what j/get does, I usually extract the js object keys using the regular cljs interop. But if that does the same as extracting the key with .-
then you should remove the parentheses in navigatior
I created the repo using https://github.com/jgoodhcg/create-expo-cljs-app
[:> navigator {:header-mode "none"}
[:> screen {:name "entrypoint"
:component (paper/withTheme screen-main)}]
[:> screen {:name "app-configuration"
:component (paper/withThem ,,,
Try with the code (edited) that I sent. (Sorry for not to put it complete, I'm in my phone)
That navigators are a little bit complex. I'm seriously thinking on creating a navigator that works well with hot-reload
Also, you could try with a small example: https://github.com/PEZ/rn-rf-shadow You would need to add the navigators on your own if you clone this repo
Also, maybe starting with ReactNative as a first approach to React is a little hard IMO, because some errors are strange and not easily to identify as when working in a web browser
when removing (),
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of <Component /> from render. Or maybe you meant to call this function rather than return it.%s,
in app$index$navigator (created by app.index.root)
in EnsureSingleNavigator
in BaseNavigationContainer
in ThemeProvider
in NavigationContainer (created by app.index.root)
in ThemeProvider (created by Provider)
in RCTView (created by View)
in View (created by Portal.Host)
in Portal.Host (created by Provider)
in Provider (created by app.index.root)
in app.index.root
in Unknown (created by ExpoRoot)
in ExpoRoot
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in main(RootComponent)
indeed, the template: https://github.com/jgoodhcg/create-expo-cljs-app/blob/master/template/src/app/index.cljs#L117
(maybe https://github.com/armincerf/kalm-mobile/blob/master/src/app/index.cljs is another example of similar navigator usage)
I'm going yo share you some code of one of my working apps, so you could look at it and replicate
[screen {:name :screen/add-package
:options {:title "Nuevo Paquete"
:icon add-package-icon-r}}
add-package/screen-fn]
;;
[screen {:name :screen/packages-list
:options {:title "Tus Paquetes"
:icon packages-list-r}}
packages-list/screen-fn]
screen
here is the same you already have in your code, so don't worry about it.
(I'll share more)(defn screen-fn [props]
(r/as-element [screen (js->clj props :keywordize-keys true)]))
There's a reason, if you pass a reactified component to the :component key
(something that is needed if you use that key), React Navigation will throw a warning about the name of the component, it's very annoying, and we cant fix it since we do not set the resulting name
the confusion I have is why my stuff "mostly works". but I will see if I can refactor, though I am also trying to understand what you changed
https://www.reddit.com/r/Clojure/comments/tp8bua/react_native_with_reagent_complains_about_react/
If you understand React, maybe the ReactNavigation docs could help you. You are pretty close to the solution
I just don't understand how it works with https://github.com/armincerf/kalm-mobile/blob/master/src/app/index.cljs where they do it the exact same way, are these examples wrong?
also in your example,
(defn screen-fn [props]
(r/as-element [screen (js->clj props :keywordize-keys true)]))
where is screen here?screen
is whatever you want, it could be your view, e.g.
(defn screen []
[ ,, ;; the view you want to mount])
Looking at the example repo you sent, they are using r/reactify-component
in the :component
key
thanks again for the help. it's really quite confusing as a beginner (only cos my stuff works with no errors/warnings, just unwanted behaviour).
I'm now in my Editor, I se why it was needed to call navigator and screen as functions
I'm going to remove the paper/withTheme
calls, because I don't know what they're doing
OK, I don't have a working enviroment, just my editor, but this should work: From stack to root:
(def stack (rn-stack/createStackNavigator))
(def navigator (.-Navigator stack))
(def screen (.-Screen stack))
(defn your-view [props]
[:> rn/View
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(defn your-view-fn [props]
(r/as-element [screen (js->clj props :keywordize-keys true)]))
(defn root []
(let [theme (<sub [:theme])
!route-name-ref (clojure.core/atom {})
!navigation-ref (clojure.core/atom {})]
[:> paper/Provider
{:theme (case theme
:light paper/DefaultTheme
:dark paper/DarkTheme
paper/DarkTheme)}
[:> nav/NavigationContainer
{:ref (fn [el] (reset! !navigation-ref el))
:on-ready (fn []
(swap! !route-name-ref merge {:current (-> @!navigation-ref
(j/call :getCurrentRoute)
(j/get :name))}))
:on-state-change (fn []
(let [prev-route-name (-> @!route-name-ref :current)
current-route-name (-> @!navigation-ref
(j/call :getCurrentRoute)
(j/get :name))]
(when (not= prev-route-name current-route-name)
;; This is where you can do side effecty things like analytics
(>evt [:some-fx-example (str "New screen encountered " current-route-name)]))
(swap! !route-name-ref merge {:current current-route-name})))}
[:> navigator {:header-mode "none"}
[:> screen {:name "entrypoint"}
your-view-fn]]]]))
Here's the code w/ the second example included:
(def stack (rn-stack/createStackNavigator))
(def navigator (.-Navigator stack))
(def screen (.-Screen stack))
(defn your-view [props]
[:> rn/View
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(defn your-view-fn [props]
(r/as-element [screen (js->clj props :keywordize-keys true)]))
;; Second example
(defn your-view-2 [props]
[:> rn/View
[:> rn/Text "Another View"]
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(def your-view-2-reactified (r/reactify-component your-view-2))
(defn root []
(let [theme (<sub [:theme])
!route-name-ref (clojure.core/atom {})
!navigation-ref (clojure.core/atom {})]
[:> paper/Provider
{:theme (case theme
:light paper/DefaultTheme
:dark paper/DarkTheme
paper/DarkTheme)}
[:> nav/NavigationContainer
{:ref (fn [el] (reset! !navigation-ref el))
:on-ready (fn []
(swap! !route-name-ref merge {:current (-> @!navigation-ref
(j/call :getCurrentRoute)
(j/get :name))}))
:on-state-change (fn []
(let [prev-route-name (-> @!route-name-ref :current)
current-route-name (-> @!navigation-ref
(j/call :getCurrentRoute)
(j/get :name))]
(when (not= prev-route-name current-route-name)
;; This is where you can do side effecty things like analytics
(>evt [:some-fx-example (str "New screen encountered " current-route-name)]))
(swap! !route-name-ref merge {:current current-route-name})))}
[:> navigator {:header-mode "none"}
[:> screen {:name "entrypoint"}
your-view-fn]
[:> screen {:name "view-2"
:component your-view-2-reactified}]]]]))
@UJA1Z6ZJ7 do you had any problems? The second example should rise a warning
here's me pasting your stuff in: https://github.com/dspearson/cljsrn-test/commit/172cccb45381edd36bd10641e741e558084724d4
› Reloading apps
Android Bundling complete 129ms
Android Running app on sdk_gphone64_x86_64
shadow-cljs #4 ready!
(and, yeah still blank.)Ok, please copy/paste this (I commented & deleted a lot of code, if a dependency in the require is wrong, please correct it)
(ns frontend.tmp
(:require
["@react-navigation/native" :as nav]
["@react-navigation/stack" :as rn-stack]
["expo" :as ex]
["expo-constants" :as expo-constants]
["react" :as react]
["react-native" :as rn]
;["react-native-paper" :as paper]
;["tailwind-rn" :default tailwind-rn]
;[applied-science.js-interop :as j]
[reagent.core :as r]
[re-frame.core :refer [dispatch-sync]]
;[shadow.expo :as expo]
;[app.views :as views]
;[app.db]
;[app.fx]
;[app.handlers]
;[app.subscriptions]
[app.helpers :refer [<sub >evt]]
)
)
(def stack (rn-stack/createStackNavigator))
(def navigator (.-Navigator stack))
(def screen (.-Screen stack))
(defn your-view [props]
[:> rn/View
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(defn your-view-fn [props]
(r/as-element [screen (js->clj props :keywordize-keys true)]))
;; Second example
(defn your-view-2 [props]
[:> rn/View
[:> rn/Text "Another View"]
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(def your-view-2-reactified (r/reactify-component your-view-2))
(defn root []
[:> nav/NavigationContainer {}
[:> navigator
[:> screen {:name "entrypoint"}
your-view-fn]
[:> screen {:name "view-2"
:component your-view-2-reactified}]]])
(defn start
{:dev/after-load true}
[]
(expo/render-root (r/as-element [root])))
(defn init []
(dispatch-sync [:initialize-db])
(dispatch-sync [:set-version version])
(start))
In your original code, from stack to root:
(def stack (rn-stack/createStackNavigator))
(def navigator (.-Navigator stack))
(def screen (.-Screen stack))
(defn your-view [props]
[:> rn/View
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(defn your-view-fn [props]
(r/as-element [your-view (js->clj props :keywordize-keys true)]))
;; Second example
(defn your-view-2 [props]
[:> rn/View
[:> rn/Text "Another View"]
[:> rn/Text (<sub [:text])]
[:> rn/TextInput {:style (tw "flex-1")
:onChangeText #(>evt [:set-text %])}]])
(def your-view-2-reactified (r/reactify-component your-view-2))
(defn root []
[:> nav/NavigationContainer {}
[:> navigator
[:> screen {:name "entrypoint"}
your-view-fn]
[:> screen {:name "view-2"
:component your-view-2-reactified}]]])
the problem was in the r/as-element
I was calling to [screen ,,
instead of [your-view ,,,
I could help you today later, is not a hard problem but I don't know what is happening without being able to run the code
https://github.com/dspearson/cljsrn-test/blob/exp/src/app/index.cljs is the latest state
I'll see if I can play around with it myself too, but also in meetings for the next few hours too, woo
I'm about to start my job right now, so maybe in about 10 hours I'll be free. I could create a simple working example w/ ReactNavigation
that'd be great. all i need to get started is a barebones example that has 2 screens, with navigation between them, and using dispatch events to live-update
@UJA1Z6ZJ7 wrote: "it's really quite confusing as a beginner (only cos my stuff works with no errors/warnings, just unwanted behaviour)." <crankyoldfart> Neither does it help a forty plus year veteran full time Lisp/Clojure developer of front ends. But this is what the rugrats have given us with their half-baked approach to software. </crankyoldfart> Seriously, gird your loins and get used to SO and this excellent Slack channel: software devs no longer take the time to write the additional code required to detect user silliness, nor even the negligible added time required to include a little info in the errors they do report. Pro tip: commit often. When your app stops working out of nowhere, copy out any useful work, stash and start over, committing in even tinier bites.
yep. just tried again with the latest version & clean templates from the example repo i was using, https://github.com/dspearson/cljsrn-test/tree/exp2 - same issue, and this one indeed automatically came with 2 screens. so i guess there is a problem with the template, or in the simple usage of subscriptions and events i am doing. c'est la vie, will wait for the simple poc UlisesMAC said he'd do later 🙂
Not sure what paper/withTheme
is doing, but it works fine if you remove it, such that screen definition would look like this:
(screen {:name "app-configuration"
:component (r/reactify-component views/app-configuration)})
indeed, seems that just the reactify-component call is the thing that was missing to make it all work as expected