Skip to content

Commit a6325e6

Browse files
committed
context: ssr & rsc
1 parent 210bb50 commit a6325e6

File tree

13 files changed

+201
-97
lines changed

13 files changed

+201
-97
lines changed

core/dev/uix/rsc_example/server/core.clj

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,9 @@
7979

8080
(def error-boundary
8181
(uix/create-error-boundary
82-
{:derive-error-state (fn [error] {:error error})}
82+
{:derive-error-state (fn [error] {:error error})
83+
:did-catch (fn [error info]
84+
(prn error))}
8385
(fn [[state] {:keys [children]}]
8486
(if-let [error (:error state)]
8587
($ server.root/html-page

core/dev/uix/rsc_example/server/ui.clj

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
(ns uix.rsc-example.server.ui
2-
(:require [cheshire.core :as json]
3-
[clojure.string :as str]
2+
(:require [clojure.string :as str]
43
[uix.core :refer [defui $] :as uix]
54
[uix.rsc :as rsc]
65
[uix.rsc-example.actions :as actions]
@@ -93,6 +92,7 @@
9392
($ :form {:action (rsc/partial actions/update-fav {:id id :intent (if liked? :remove :add)})}
9493
($ ui/fav-button {:liked? liked?}))))
9594

95+
9696
;; todo: reload client when server updates, in dev
9797
(defui movie-title [{:keys [id]}]
9898
(let [{:movies/keys [id title year thumbnail extract]
@@ -133,7 +133,7 @@
133133
(let [ids [32932, 23643, 29915, 30895, 31472, 33411]]
134134
($ movie-grid
135135
(for [id ids]
136-
($ movie-title {:key id :id id})))))
136+
($ movie-title {:id id})))))
137137

138138
(defui actor [{:keys [params]}]
139139
(let [{:keys [id]} params
@@ -147,7 +147,7 @@
147147
name))
148148
($ movie-grid
149149
(for [id movie_ids]
150-
($ movie-title {:key id :id id}))))))
150+
($ movie-title {:id id}))))))
151151

152152
(defui img [{:keys [thumbnail]}]
153153
($ :img {:class "h-[435px] object-cover mb-4"
@@ -171,6 +171,6 @@
171171
($ :div
172172
(->>
173173
(for [id cast_ids]
174-
($ :span {:key id}
174+
($ :span
175175
($ actor-link {:id id})))
176176
(interpose ($ :span.mx-1 "")))))))))

core/scripts/rsc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ npm ci
88

99
echo "Starting examples build server..."
1010
npx concurrently "npx patch-package && clojure -A:examples -M -m shadow.cljs.devtools.cli watch rsc" "npx @tailwindcss/cli -i ./dev/uix/main.css -o ./rsc-out/main.css --watch"
11+
#npx concurrently "npx patch-package && clojure -A:examples -M -m shadow.cljs.devtools.cli release rsc" "npx @tailwindcss/cli -i ./dev/uix/main.css -o ./rsc-out/main.css --minify"

core/src/uix/compiler/aot.clj

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,10 +119,21 @@
119119
[spread-props]))))
120120
v)))
121121

122+
(defn- context-element? [[tag] env]
123+
(and (symbol? tag)
124+
(some-> (resolve env tag) meta :uix/context)))
125+
126+
(defn- compile-context-provider [[var {:keys [value]} & children]]
127+
[:uix/context `(fn [f#] (binding [~var ~value]
128+
(f#)))
129+
(vec children)])
130+
122131
(defn compile-element [v {:keys [env] :as opts}]
123132
(if (uix.lib/cljs-env? env)
124133
(compile-element* v opts)
125-
(with-meta (with-spread-props v) {:uix/element? true})))
134+
(if (context-element? v env)
135+
(compile-context-provider v)
136+
(with-meta (with-spread-props v) {:uix/element? true}))))
126137

127138
;; ========== forms rewriter ==========
128139

core/src/uix/core.clj

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,24 @@
245245
(defn use-ref [value]
246246
(hooks/use-ref value))
247247

248+
(defn- create-context-with-ref [name value env]
249+
`(def ~(with-meta name {:dynamic true})
250+
(let [ctx# (create-context ~value)]
251+
(swap! context-reg assoc ~(str (-> env :ns :name) "/" name) ctx#)
252+
ctx#)))
253+
254+
(defmacro defcontext
255+
"cljs: Creates React context with initial value set to `value`.
256+
clj: Create dynamic var bound to `value`."
257+
([name]
258+
(if (uix.lib/cljs-env? &env)
259+
(create-context-with-ref name nil &env)
260+
`(def ~(with-meta name {:dynamic true :uix/context true}))))
261+
([name value]
262+
(if (uix.lib/cljs-env? &env)
263+
(create-context-with-ref name value &env)
264+
`(def ~(with-meta name {:dynamic true :uix/context true}) ~value))))
265+
248266
(defn use-context [value]
249267
(hooks/use-context value))
250268

@@ -360,7 +378,7 @@
360378
`(uix.hooks.alpha/use-imperative-handle ~ref ~f ~(->js-deps deps))))
361379

362380
(defui suspense [{:keys [fallback children] :as props}]
363-
[::suspense props])
381+
[:uix/suspense props])
364382

365383
(defui strict-mode [{:keys [children]}]
366384
children)

core/src/uix/core.cljs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,8 @@
129129
(set! (.-current ^js this) (apply f (.-current ^js this) a b xs)))))))
130130
(.-current ref))))
131131

132+
(def context-reg (atom {}))
133+
132134
(defn create-context
133135
"Creates React Context with an optional default value"
134136
([]

core/src/uix/hooks/alpha.clj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,7 @@
4949
(f))
5050

5151
;; == Context hook ==
52-
;; TODO: requires changes tp how context is created
5352
(defn use-context [v]
54-
(throw (UnsupportedOperationException. "use-context is not implemented yet"))
5553
v)
5654

5755
;; == Imperative Handle hook ==

core/src/uix/rsc.cljc

Lines changed: 43 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -26,23 +26,28 @@
2626
(fn [props]
2727
(let [rsc-props (aget props "rsc/props")
2828
rsc-refs (aget props "rsc/refs")
29+
children (aget props "children")
2930
props (uix/use-memo
30-
#(walk/postwalk
31-
(fn [form]
32-
;; todo: maybe use data readers?
33-
(cond
34-
(and (string? form)
35-
(.startsWith form "$")
36-
rsc-refs)
37-
(let [ref (aget rsc-refs form)]
38-
(when ^boolean goog/DEBUG
39-
(when (nil? ref)
40-
(js/console.error "server reference " ref " is not registered")))
41-
ref)
42-
43-
:else form))
44-
(edn/read-string rsc-props))
45-
[rsc-props rsc-refs])]
31+
#(let [{:uix/keys [context] :as edn-props} (edn/read-string rsc-props)
32+
edn-props (walk/postwalk
33+
(fn [form]
34+
;; todo: maybe use data readers?
35+
(cond
36+
(and (string? form)
37+
(.startsWith form "$")
38+
rsc-refs)
39+
(let [ref (aget rsc-refs form)]
40+
(when ^boolean goog/DEBUG
41+
(when (nil? ref)
42+
(js/console.error "server reference " ref " is not registered")))
43+
ref)
44+
45+
:else form))
46+
(dissoc edn-props :uix/context))]
47+
(if (and context children)
48+
(assoc edn-props :children children :uix/context context)
49+
edn-props))
50+
[rsc-props rsc-refs children])]
4651
(uix.core/$ uix-comp props)))))
4752

4853
#?(:cljs
@@ -170,31 +175,32 @@
170175
:margin-bottom 24}}
171176
(.-message error))
172177
(let [{:keys [src line start-line frame-line name]}
173-
(bean/bean (or js/window.__ERROR_SRC (.-digest error)))]
178+
(bean/bean (or js/window.__ERROR_SRC (.-digest error) #js {}))]
174179
($ :<>
175180
($ :div {:style {:font-size 12
176181
:color "#5a5a5a"}}
177182
name)
178-
($ :pre {:style {:font-size 12
179-
:max-height 360
180-
:overflow-y :auto
181-
:background-color "rgba(206, 17, 38, 0.05)"
182-
:margin "16px 0"
183-
:padding "8px 0"
184-
:border-radius 5}}
185-
($ :code
186-
(->> (str/split-lines src)
187-
(map-indexed (fn [idx ln]
188-
(if (= idx line)
189-
($ :div {:key idx
190-
:style {:background-color "#ff000047"
191-
:padding "0 8px"}}
192-
(str (+ start-line idx) " ")
193-
ln)
194-
($ :div {:key idx
195-
:style {:padding "0 8px"}}
196-
(str (+ start-line idx) " ")
197-
ln)))))))
183+
(when src
184+
($ :pre {:style {:font-size 12
185+
:max-height 360
186+
:overflow-y :auto
187+
:background-color "rgba(206, 17, 38, 0.05)"
188+
:margin "16px 0"
189+
:padding "8px 0"
190+
:border-radius 5}}
191+
($ :code
192+
(->> (str/split-lines src)
193+
(map-indexed (fn [idx ln]
194+
(if (= idx line)
195+
($ :div {:key idx
196+
:style {:background-color "#ff000047"
197+
:padding "0 8px"}}
198+
(str (+ start-line idx) " ")
199+
ln)
200+
($ :div {:key idx
201+
:style {:padding "0 8px"}}
202+
(str (+ start-line idx) " ")
203+
ln))))))))
198204
($ :pre {:style {:font-size 12
199205
:max-height 360
200206
:overflow-y :auto

core/src/uix/rsc/context.cljc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
(ns uix.rsc.context
2+
(:require [uix.core :refer [defui $]]))
3+
4+
#?(:cljs
5+
(defn- with-providers [context children]
6+
(if (seq context)
7+
(let [[[k v] & context] context
8+
el (@uix.core/context-reg k)]
9+
($ el {:value v}
10+
(with-providers context children)))
11+
children)))
12+
13+
(defui ^:client context-provider
14+
[{:keys [children]
15+
:uix/keys [context]}]
16+
#?(:clj children
17+
:cljs (with-providers context children)))

core/test/uix/hooks_test.clj

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,6 @@
4141
(deftest test-use-memo
4242
(is (= 1 (uix/use-memo #(inc 0) []))))
4343

44-
(deftest test-use-memo
45-
(is (thrown-with-msg? UnsupportedOperationException
46-
#"use-context is not implemented yet"
47-
(uix/use-context 1))))
48-
4944
(deftest test-use-imperative-handle
5045
(let [called? (atom false)]
5146
(is (nil? (uix/use-imperative-handle nil #(reset! called? true))))

0 commit comments

Comments
 (0)