Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
(ns example.app
(:require
["expo" :as ex]
["react-native" :as rn]
["react" :as react :rename {createElement $}]
[shadow.expo :as expo]))
(defn useEffectiveReducer [reducer handlers initial-state]
(let [[state set-state] (react/useState initial-state)
[queue update-queue] (react/useReducer (fn [state action] (action state)) [])
dispatch (fn [action]
(let [{:keys [state] :as effect} (reducer state action)]
(when state ;; Todo: check for not= state new-state. Also it can be nil so check for contains?
(set-state state))
(when-let [effect (not-empty (dissoc effect :state))]
(update-queue #(conj % effect)))))]
(react/useEffect
(fn []
(println :Effect! queue)
(doseq [effect queue
[type payload] effect
;; Todo: check for nil
:let [handler (get handlers type)]]
(handler dispatch payload))
(update-queue (constantly []))
(fn []))
#js [queue])
[state dispatch]))
(defmulti reducer (fn [state {:keys [type payload]}] type))
(defmethod reducer ::login [state {{:keys [email password]} :payload :as action}]
{:state "Loading..."
:http {:url :login-url
:params (str email ":" password)
:callback ::login-cb}
:log action})
(defmethod reducer ::login-cb [state {{:keys [success body]} :payload :as action}]
{:state (if success
(str "Your name is " (get-in body [:profile :name]))
"Call to server failed")
:log action})
(defmethod reducer ::logout [state action]
{:state "Loading..."
:http {:url :logout-url
:callback ::logout-cb}
:log action})
(defmethod reducer ::logout-cb [state {{:keys [success]} :payload :as action}]
{:state "Logged out"
:log action})
(defn http [dispatch {:keys [callback] :as payload}]
(js/setTimeout
#(do (println "Calling server.." payload)
(dispatch {:type callback
:payload {:success true
:status 200
:body {:profile {:name "Alice"}}}}))
1000))
(defn log [dispatch payload]
(println payload))
(defn App []
(let [[state dispatch] (useEffectiveReducer reducer {:http http :log log} "Logged out")]
;; Todo: create useReloadAtom effect
(println :Rendering state)
($ rn/View #js {:style #js {:flex 1}}
($ rn/Text #js {} (str "Current status: " state))
($ rn/Button #js {:title "Login"
:onPress #(dispatch {:type ::login
:payload {:email "foo@gmail.com"
:password "123"}})})
($ rn/Button #js {:title "Logout"
:onPress #(dispatch {:type ::logout})}))))
(defn start
{:dev/after-load true}
[]
(expo/render-root ($ App)))
(defn ^:export init []
(start))
(defn stop
{:dev/before-load true}
[]
(js/console.log "stop"))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment