Skip to content

Instantly share code, notes, and snippets.

@danielneal
Created June 10, 2019 15:56
Show Gist options
  • Save danielneal/a09770486a0f0d53367d775b8ccdff93 to your computer and use it in GitHub Desktop.
Save danielneal/a09770486a0f0d53367d775b8ccdff93 to your computer and use it in GitHub Desktop.
Shadow / hx / React Native / expo
(ns test.app
(:require
["expo" :as ex]
["react-native" :as rn]
["react" :as react]
[test.db :as db]
[hx.react :as hx :refer [defnc]]
[hx.hooks :as hooks]
[shadow.expo :as expo]))
(defnc Greet []
;; use React Hooks for state management
(let [{:person/keys [name]} (db/useQuery [:person/name])
dispatch! (db/useDispatch)]
[:<>
[rn/Text name]
[rn/TextInput {:onChangeText (fn [text] (dispatch! [:person/set-name text]))}]]))
(defnc App []
[:provider db/provider
[rn/View {:style {:padding 8}}
[Greet]]])
(defn start
{:dev/after-load true}
[]
(expo/render-root (hx/f [App])))
(defn init []
(start))
(ns test.db
(:require [hx.react :as hx]
[hx.hooks :as hooks]
["react" :as react]))
;; little query engine over nested maps
(defn paths
([q]
(paths [] {} q))
([current-path m q]
(letfn [(paths-map
[current-path m q]
(apply merge (for [[k v] q]
(paths (conj current-path k) {} v))))
(paths-vec
[current-path m q]
(apply merge (for [v q]
(if (map? v)
(paths-map current-path {} v)
(paths (conj current-path v) {} v)))))]
(cond
(map? q) (paths-map current-path m q)
(vector? q) (paths-vec current-path m q)
:else {q current-path}))))
(defn query-fn
[q]
(let [paths (paths q)]
(fn [db]
(reduce-kv (fn [m k v] (assoc m k (get-in db v))) {} paths))))
(defn query
[db q]
(let [f (query-fn q)]
(f db)))
;; (query {:a 1 :b 2 :c {:d 3}} [:a]) => {:a 1})
;; (query {:a 1, :b 2 :c {:d 3}} [:a {:b :product/id}]
(def context (hx/create-context))
;; Subscriptions - provide a hook that returns some state that
;; runs query over db and updates state when it changes
(defn useQuery
[q]
(let [db (hooks/useContext context)
[result updateResult] (react/useState (query @db q))
k (gensym)]
(hooks/useEffect
(fn []
(add-watch db k (fn [_ _ _ state]
(let [new-result (query state q)]
(when (not= new-result result)
(updateResult new-result)))))
(fn []
(remove-watch db k)))
#js [q])
result))
;; Dispatch - provide a hook that returns a fn to swap! the db
(defmulti handle-event (fn [db [type & _]] type))
(defmethod handle-event :person/set-name
[db [_ v]]
(assoc db :person/name v))
;; Context Provider
(def app-db (atom {}))
(def provider
{:context context
:value app-db})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment