mirror of
https://github.com/status-im/reagent.git
synced 2025-02-20 23:58:19 +00:00
Merge pull request #325 from reagent-project/fix-array-ops
Fix aget/aset use with objects
This commit is contained in:
commit
88e9833be9
@ -11,6 +11,12 @@ ensure `:class` is merged correctly when it is defined as collection. ([#412](ht
|
||||
- Add `reagent.core/class-names` utility functions which can be used
|
||||
to normalize and combine `:class` values (similar to `classnames` JS library)
|
||||
- Fix comparing Reagent `PartialFn` to `nil` ([#385](https://github.com/reagent-project/reagent/issues/385))
|
||||
- Reagent no longer abuses `aget` or `aset` for accessing objects, and instead
|
||||
uses correct Object interop forms, allowing use of ClojureScript `:checked-arrays :warn` option.
|
||||
- **Removed `reagent.interop` namespace**
|
||||
- These macros where bad practice and don't work properly if
|
||||
React code is optimized by Closure. Proper object interop forms or `goog.object` functions
|
||||
should be used instead.
|
||||
|
||||
## 0.8.1 (2018-05-15)
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
(:require [clojure.string :as string]
|
||||
[goog.events :as evt]
|
||||
[reagent.core :as r]
|
||||
[reagent.debug :refer-macros [dbg log dev?]]
|
||||
[reagent.interop :as i :refer-macros [$ $!]])
|
||||
[reagent.debug :refer-macros [dbg log dev?]])
|
||||
(:import goog.History
|
||||
[goog.history Html5History EventType]))
|
||||
|
||||
|
@ -217,8 +217,6 @@ In the code sample above, notice that the renderer function is identified via an
|
||||
|
||||
Its a trap to mistakenly use `:render` because you won't get any errors, **except** the function you supply will only ever be called with one parameter, and it won't be the one you expect. [Some details here](https://github.com/reagent-project/reagent/issues/47#issuecomment-61056999).
|
||||
|
||||
**Note:** prior to version 0.5.0 you had to use the key `:component-function` instead of `:reagent-render`.
|
||||
|
||||
**Rookie mistake**
|
||||
|
||||
While you can override `component-should-update` to achieve some performance improvements, you probably shouldn't unless you really, really know what you are doing. Resist the urge. Your current performance is just fine. :-)
|
||||
|
28
project.clj
28
project.clj
@ -12,7 +12,7 @@
|
||||
[cljsjs/react-dom-server "16.6.0-0"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-doo "0.1.10"]
|
||||
[lein-doo "0.1.11"]
|
||||
[lein-codox "0.10.3"]
|
||||
[lein-figwheel "0.5.18"]]
|
||||
|
||||
@ -24,7 +24,7 @@
|
||||
|
||||
:profiles {:dev {:dependencies [[org.clojure/clojurescript "1.10.439"]
|
||||
[figwheel "0.5.18"]
|
||||
[doo "0.1.10"]
|
||||
[doo "0.1.11"]
|
||||
[cljsjs/prop-types "15.6.2-0"]]
|
||||
:source-paths ["demo" "test" "examples/todomvc/src" "examples/simple/src" "examples/geometry/src"]
|
||||
:resource-paths ["site" "target/cljsbuild/client" "target/cljsbuild/client-npm"]}}
|
||||
@ -53,7 +53,8 @@
|
||||
:output-dir "target/cljsbuild/client/public/js/out"
|
||||
:output-to "target/cljsbuild/client/public/js/main.js"
|
||||
:npm-deps false
|
||||
:asset-path "js/out"}}
|
||||
:asset-path "js/out"
|
||||
:checked-arrays :warn}}
|
||||
|
||||
{:id "client-npm"
|
||||
:source-paths ["demo"]
|
||||
@ -65,7 +66,8 @@
|
||||
:output-dir "target/cljsbuild/client-npm/public/js/out"
|
||||
:output-to "target/cljsbuild/client-npm/public/js/main.js"
|
||||
:npm-deps true
|
||||
:asset-path "js/out"}}
|
||||
:asset-path "js/out"
|
||||
:checked-arrays :warn}}
|
||||
|
||||
{:id "test"
|
||||
:source-paths ["test"]
|
||||
@ -76,7 +78,8 @@
|
||||
:output-dir "target/cljsbuild/test/out"
|
||||
:output-to "target/cljsbuild/test/main.js"
|
||||
:npm-deps false
|
||||
:aot-cache true}}
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}
|
||||
|
||||
{:id "test-npm"
|
||||
:source-paths ["test"]
|
||||
@ -87,7 +90,8 @@
|
||||
:output-dir "target/cljsbuild/test-npm/out"
|
||||
:output-to "target/cljsbuild/test-npm/main.js"
|
||||
:npm-deps true
|
||||
:aot-cache true}}
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}
|
||||
|
||||
;; Separate source-path as this namespace uses Node built-in modules which
|
||||
;; aren't available for other targets, and would break other builds.
|
||||
@ -110,7 +114,8 @@
|
||||
:output-dir "target/cljsbuild/node-test/out"
|
||||
:output-to "target/cljsbuild/node-test/main.js"
|
||||
:npm-deps false
|
||||
:aot-cache true}}
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}
|
||||
|
||||
{:id "node-test-npm"
|
||||
:source-paths ["test/reagenttest/runtests.cljs"]
|
||||
@ -122,7 +127,8 @@
|
||||
:output-dir "target/cljsbuild/node-test-npm/out"
|
||||
:output-to "target/cljsbuild/node-test-npm/main.js"
|
||||
:npm-deps true
|
||||
:aot-cache true}}
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}
|
||||
|
||||
;; With :advanched source-paths doesn't matter that much as
|
||||
;; Cljs compiler will only read :main file.
|
||||
@ -162,7 +168,8 @@
|
||||
:output-dir "target/cljsbuild/prod-test/out"
|
||||
:closure-warnings {:global-this :off}
|
||||
:npm-deps false
|
||||
:aot-cache true}}
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}
|
||||
|
||||
{:id "prod-test-npm"
|
||||
:source-paths ["test"]
|
||||
@ -175,4 +182,5 @@
|
||||
:output-dir "target/cljsbuild/prod-test-npm/out"
|
||||
:closure-warnings {:global-this :off}
|
||||
:npm-deps true
|
||||
:aot-cache true}}]})
|
||||
:aot-cache true
|
||||
:checked-arrays :warn}}]})
|
||||
|
@ -11,7 +11,6 @@
|
||||
assert-some assert-component
|
||||
assert-js-object assert-new-state
|
||||
assert-callable]]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[reagent.dom :as dom]))
|
||||
|
||||
(def is-client util/is-client)
|
||||
@ -104,8 +103,7 @@
|
||||
(batch/flush-after-render))
|
||||
|
||||
(defn create-class
|
||||
"Create a component, React style. Should be called with a map,
|
||||
looking like this:
|
||||
"Creates JS class based on provided Clojure map, for example:
|
||||
|
||||
```cljs
|
||||
{:get-initial-state (fn [this])
|
||||
@ -119,7 +117,13 @@
|
||||
:reagent-render (fn [args....])} ;; or :render (fn [this])
|
||||
```
|
||||
|
||||
Everything is optional, except either :reagent-render or :render."
|
||||
Everything is optional, except either :reagent-render or :render.
|
||||
|
||||
Map keys should use `React.Component` method names (https://reactjs.org/docs/react-component.html),
|
||||
and can be provided in snake-case or camelCase.
|
||||
Constructor function is defined using key `:get-initial-state`.
|
||||
|
||||
React built-in static methods or properties are automatically defined as statics."
|
||||
[spec]
|
||||
(comp/create-class spec))
|
||||
|
||||
|
@ -4,8 +4,7 @@
|
||||
[reagent.impl.template :as tmpl]
|
||||
[reagent.impl.batching :as batch]
|
||||
[reagent.ratom :as ratom]
|
||||
[reagent.debug :refer-macros [dbg]]
|
||||
[reagent.interop :refer-macros [$ $!]]))
|
||||
[reagent.debug :refer-macros [dbg]]))
|
||||
|
||||
(defonce ^:private imported nil)
|
||||
|
||||
|
@ -2,8 +2,7 @@
|
||||
(:require ["react-dom/server" :as dom-server]
|
||||
[reagent.impl.util :as util]
|
||||
[reagent.impl.template :as tmpl]
|
||||
[reagent.ratom :as ratom]
|
||||
[reagent.interop :refer-macros [$ $!]]))
|
||||
[reagent.ratom :as ratom]))
|
||||
|
||||
(defonce ^:private imported nil)
|
||||
|
||||
|
@ -1,9 +1,7 @@
|
||||
(ns reagent.impl.batching
|
||||
(:refer-clojure :exclude [flush])
|
||||
(:require [reagent.debug :refer-macros [dbg assert-some]]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[reagent.impl.util :refer [is-client]]
|
||||
[clojure.string :as string]))
|
||||
(:require [reagent.debug :refer-macros [assert-some]]
|
||||
[reagent.impl.util :refer [is-client]]))
|
||||
|
||||
;;; Update batching
|
||||
|
||||
@ -19,15 +17,15 @@
|
||||
(if-not is-client
|
||||
fake-raf
|
||||
(let [w js/window]
|
||||
(or ($ w :requestAnimationFrame)
|
||||
($ w :webkitRequestAnimationFrame)
|
||||
($ w :mozRequestAnimationFrame)
|
||||
($ w :msRequestAnimationFrame)
|
||||
(or (.-requestAnimationFrame w)
|
||||
(.-webkitRequestAnimationFrame w)
|
||||
(.-mozRequestAnimationFrame w)
|
||||
(.-msRequestAnimationFrame w)
|
||||
fake-raf))))
|
||||
|
||||
(defn compare-mount-order [c1 c2]
|
||||
(- ($ c1 :cljsMountOrder)
|
||||
($ c2 :cljsMountOrder)))
|
||||
(- (.-cljsMountOrder c1)
|
||||
(.-cljsMountOrder c2)))
|
||||
|
||||
(defn run-queue [a]
|
||||
;; sort components by mount order, to make sure parents
|
||||
@ -35,55 +33,67 @@
|
||||
(.sort a compare-mount-order)
|
||||
(dotimes [i (alength a)]
|
||||
(let [c (aget a i)]
|
||||
(when (true? ($ c :cljsIsDirty))
|
||||
($ c forceUpdate)))))
|
||||
(when (true? (.-cljsIsDirty c))
|
||||
(.forceUpdate c)))))
|
||||
|
||||
|
||||
;; Set from ratom.cljs
|
||||
(defonce ratom-flush (fn []))
|
||||
|
||||
(defn run-funs [fs]
|
||||
(dotimes [i (alength fs)]
|
||||
((aget fs i))))
|
||||
|
||||
(defn enqueue [queue fs f]
|
||||
(assert-some f "Enqueued function")
|
||||
(.push fs f)
|
||||
(.schedule queue))
|
||||
|
||||
(deftype RenderQueue [^:mutable ^boolean scheduled?]
|
||||
Object
|
||||
(enqueue [this k f]
|
||||
(assert-some f "Enqueued function")
|
||||
(when (nil? (aget this k))
|
||||
(aset this k (array)))
|
||||
(.push (aget this k) f)
|
||||
(.schedule this))
|
||||
|
||||
(run-funs [this k]
|
||||
(when-some [fs (aget this k)]
|
||||
(aset this k nil)
|
||||
(dotimes [i (alength fs)]
|
||||
((aget fs i)))))
|
||||
|
||||
(schedule [this]
|
||||
(when-not scheduled?
|
||||
(set! scheduled? true)
|
||||
(next-tick #(.run-queues this))))
|
||||
|
||||
(queue-render [this c]
|
||||
(.enqueue this "componentQueue" c))
|
||||
(when (nil? (.-componentQueue this))
|
||||
(set! (.-componentQueue this) (array)))
|
||||
(enqueue this (.-componentQueue this) c))
|
||||
|
||||
(add-before-flush [this f]
|
||||
(.enqueue this "beforeFlush" f))
|
||||
(when (nil? (.-beforeFlush this))
|
||||
(set! (.-beforeFlush this) (array)))
|
||||
(enqueue this (.-beforeFlush this) f))
|
||||
|
||||
(add-after-render [this f]
|
||||
(.enqueue this "afterRender" f))
|
||||
(when (nil? (.-afterRender this))
|
||||
(set! (.-afterRender this) (array)))
|
||||
(enqueue this (.-afterRender this) f))
|
||||
|
||||
(run-queues [this]
|
||||
(set! scheduled? false)
|
||||
(.flush-queues this))
|
||||
|
||||
(flush-before-flush [this]
|
||||
(when-some [fs (.-beforeFlush this)]
|
||||
(set! (.-beforeFlush this) nil)
|
||||
(run-funs fs)))
|
||||
|
||||
(flush-render [this]
|
||||
(when-some [fs (.-componentQueue this)]
|
||||
(set! (.-componentQueue this) nil)
|
||||
(run-queue fs)))
|
||||
|
||||
(flush-after-render [this]
|
||||
(.run-funs this "afterRender"))
|
||||
(when-some [fs (.-afterRender this)]
|
||||
(set! (.-afterRender this) nil)
|
||||
(run-funs fs)))
|
||||
|
||||
(flush-queues [this]
|
||||
(.run-funs this "beforeFlush")
|
||||
(.flush-before-flush this)
|
||||
(ratom-flush)
|
||||
(when-some [cs (aget this "componentQueue")]
|
||||
(aset this "componentQueue" nil)
|
||||
(run-queue cs))
|
||||
(.flush-render this)
|
||||
(.flush-after-render this)))
|
||||
|
||||
(defonce render-queue (->RenderQueue false))
|
||||
@ -95,12 +105,12 @@
|
||||
(.flush-after-render render-queue))
|
||||
|
||||
(defn queue-render [c]
|
||||
(when-not ($ c :cljsIsDirty)
|
||||
($! c :cljsIsDirty true)
|
||||
(when-not (.-cljsIsDirty c)
|
||||
(set! (.-cljsIsDirty c) true)
|
||||
(.queue-render render-queue c)))
|
||||
|
||||
(defn mark-rendered [c]
|
||||
($! c :cljsIsDirty false))
|
||||
(set! (.-cljsIsDirty c) false))
|
||||
|
||||
(defn do-before-flush [f]
|
||||
(.add-before-flush render-queue f))
|
||||
|
@ -4,9 +4,9 @@
|
||||
[reagent.impl.util :as util]
|
||||
[reagent.impl.batching :as batch]
|
||||
[reagent.ratom :as ratom]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[reagent.debug :refer-macros [dbg prn dev? warn error warn-unless
|
||||
assert-callable]]))
|
||||
assert-callable]]
|
||||
[goog.object :as gobj]))
|
||||
|
||||
(declare ^:dynamic *current-component*)
|
||||
|
||||
@ -16,10 +16,12 @@
|
||||
(defn shallow-obj-to-map [o]
|
||||
(let [ks (js-keys o)
|
||||
len (alength ks)]
|
||||
(loop [m {} i 0]
|
||||
(loop [m {}
|
||||
i 0]
|
||||
(if (< i len)
|
||||
(let [k (aget ks i)]
|
||||
(recur (assoc m (keyword k) (aget o k)) (inc i)))
|
||||
(recur (assoc m (keyword k) (gobj/get o k))
|
||||
(inc i)))
|
||||
m))))
|
||||
|
||||
(defn extract-props [v]
|
||||
@ -33,52 +35,52 @@
|
||||
(subvec v first-child))))
|
||||
|
||||
(defn props-argv [c p]
|
||||
(if-some [a ($ p :argv)]
|
||||
(if-some [a (.-argv p)]
|
||||
a
|
||||
[(.-constructor c) (shallow-obj-to-map p)]))
|
||||
|
||||
(defn get-argv [c]
|
||||
(props-argv c ($ c :props)))
|
||||
(props-argv c (.-props c)))
|
||||
|
||||
(defn get-props [c]
|
||||
(let [p ($ c :props)]
|
||||
(if-some [v ($ p :argv)]
|
||||
(let [p (.-props c)]
|
||||
(if-some [v (.-argv p)]
|
||||
(extract-props v)
|
||||
(shallow-obj-to-map p))))
|
||||
|
||||
(defn get-children [c]
|
||||
(let [p ($ c :props)]
|
||||
(if-some [v ($ p :argv)]
|
||||
(let [p (.-props c)]
|
||||
(if-some [v (.-argv p)]
|
||||
(extract-children v)
|
||||
(->> ($ p :children)
|
||||
(->> (.-children p)
|
||||
(react/Children.toArray)
|
||||
(into [])))))
|
||||
|
||||
(defn ^boolean reagent-class? [c]
|
||||
(and (fn? c)
|
||||
(some? (some-> c .-prototype ($ :reagentRender)))))
|
||||
(some? (some-> c (.-prototype) (.-reagentRender)))))
|
||||
|
||||
(defn ^boolean react-class? [c]
|
||||
(and (fn? c)
|
||||
(some? (some-> c .-prototype ($ :render)))))
|
||||
(some? (some-> c (.-prototype) (.-render)))))
|
||||
|
||||
(defn ^boolean reagent-component? [c]
|
||||
(some? ($ c :reagentRender)))
|
||||
(some? (.-reagentRender c)))
|
||||
|
||||
(defn cached-react-class [c]
|
||||
($ c :cljsReactClass))
|
||||
(.-cljsReactClass c))
|
||||
|
||||
(defn cache-react-class [c constructor]
|
||||
($! c :cljsReactClass constructor))
|
||||
(set! (.-cljsReactClass c) constructor))
|
||||
|
||||
|
||||
;;; State
|
||||
|
||||
(defn state-atom [this]
|
||||
(let [sa ($ this :cljsState)]
|
||||
(let [sa (.-cljsState this)]
|
||||
(if-not (nil? sa)
|
||||
sa
|
||||
($! this :cljsState (ratom/atom nil)))))
|
||||
(set! (.-cljsState this) (ratom/atom nil)))))
|
||||
|
||||
;; avoid circular dependency: this gets set from template.cljs
|
||||
(defonce as-element nil)
|
||||
@ -94,9 +96,12 @@
|
||||
and calls wrap-render again (`recur`), until the render result doesn't evaluate to a function.
|
||||
3) Anything else - Returns the result of evaluating `c`"
|
||||
[c]
|
||||
(let [f ($ c :reagentRender)
|
||||
(let [f (.-reagentRender c)
|
||||
_ (assert-callable f)
|
||||
res (if (true? ($ c :cljsLegacyRender))
|
||||
;; cljsLegacyRender tells if this calls was defined
|
||||
;; using :render instead of :reagent-render
|
||||
;; in that case, the :render fn is called with just `this` as argument.
|
||||
res (if (true? (.-cljsLegacyRender c))
|
||||
(.call f c c)
|
||||
(let [v (get-argv c)
|
||||
n (count v)]
|
||||
@ -113,7 +118,7 @@
|
||||
(fn [& args]
|
||||
(as-element (apply vector res args)))
|
||||
res)]
|
||||
($! c :reagentRender f)
|
||||
(set! (.-reagentRender c) f)
|
||||
(recur c))
|
||||
:else res)))
|
||||
|
||||
@ -142,9 +147,10 @@
|
||||
(def static-fns
|
||||
{:render
|
||||
(fn render []
|
||||
;; TODO: Use static property for cljsRatom
|
||||
(this-as c (if util/*non-reactive*
|
||||
(do-render c)
|
||||
(let [rat ($ c :cljsRatom)]
|
||||
(let [rat (gobj/get c "cljsRatom")]
|
||||
(batch/mark-rendered c)
|
||||
(if (nil? rat)
|
||||
(ratom/run-in-reaction #(do-render c) c "cljsRatom"
|
||||
@ -171,8 +177,8 @@
|
||||
(this-as c
|
||||
;; Don't care about nextstate here, we use forceUpdate
|
||||
;; when only when state has changed anyway.
|
||||
(let [old-argv ($ c :props.argv)
|
||||
new-argv ($ nextprops :argv)
|
||||
(let [old-argv (.. c -props -argv)
|
||||
new-argv (.-argv nextprops)
|
||||
noargv (or (nil? old-argv) (nil? new-argv))]
|
||||
(cond
|
||||
(nil? f) (or noargv (try (not= old-argv new-argv)
|
||||
@ -193,7 +199,7 @@
|
||||
:componentWillMount
|
||||
(fn componentWillMount []
|
||||
(this-as c
|
||||
($! c :cljsMountOrder (batch/next-mount-count))
|
||||
(set! (.-cljsMountOrder c) (batch/next-mount-count))
|
||||
(when-not (nil? f)
|
||||
(.call f c c))))
|
||||
|
||||
@ -204,8 +210,7 @@
|
||||
:componentWillUnmount
|
||||
(fn componentWillUnmount []
|
||||
(this-as c
|
||||
(some-> ($ c :cljsRatom)
|
||||
ratom/dispose!)
|
||||
(some-> (gobj/get c "cljsRatom") ratom/dispose!)
|
||||
(batch/mark-rendered c)
|
||||
(when-not (nil? f)
|
||||
(.call f c c))))
|
||||
@ -216,12 +221,14 @@
|
||||
|
||||
nil))
|
||||
|
||||
(defn get-wrapper [key f name]
|
||||
(defn get-wrapper [key f]
|
||||
(let [wrap (custom-wrapper key f)]
|
||||
(when (and wrap f)
|
||||
(assert-callable f))
|
||||
(or wrap f)))
|
||||
|
||||
;; Though the value is nil here, the wrapper function will be
|
||||
;; added to class to manage Reagent ratom lifecycle.
|
||||
(def obligatory {:shouldComponentUpdate nil
|
||||
:componentWillMount nil
|
||||
:componentWillUnmount nil})
|
||||
@ -238,23 +245,20 @@
|
||||
|
||||
(defn wrap-funs [fmap]
|
||||
(when (dev?)
|
||||
(let [renders (select-keys fmap [:render :reagentRender :componentFunction])
|
||||
(let [renders (select-keys fmap [:render :reagentRender])
|
||||
render-fun (-> renders vals first)]
|
||||
(assert (not (:componentFunction fmap)) ":component-function is no longer supported, use :reagent-render instead.")
|
||||
(assert (pos? (count renders)) "Missing reagent-render")
|
||||
(assert (== 1 (count renders)) "Too many render functions supplied")
|
||||
(assert-callable render-fun)))
|
||||
(let [render-fun (or (:reagentRender fmap)
|
||||
(:componentFunction fmap))
|
||||
legacy-render (nil? render-fun)
|
||||
render-fun (or render-fun
|
||||
(:render fmap))
|
||||
name (str (or (:displayName fmap)
|
||||
(util/fun-name render-fun)))
|
||||
name (case name
|
||||
"" (str (gensym "reagent"))
|
||||
name)
|
||||
legacy-render (nil? (:reagentRender fmap))
|
||||
name (or (:displayName fmap)
|
||||
(util/fun-name render-fun)
|
||||
(str (gensym "reagent")))
|
||||
fmap (reduce-kv (fn [m k v]
|
||||
(assoc m k (get-wrapper k v name)))
|
||||
(assoc m k (get-wrapper k v)))
|
||||
{} fmap)]
|
||||
(assoc fmap
|
||||
:displayName name
|
||||
@ -265,7 +269,7 @@
|
||||
(defn map-to-js [m]
|
||||
(reduce-kv (fn [o k v]
|
||||
(doto o
|
||||
(aset (name k) v)))
|
||||
(gobj/set (name k) v)))
|
||||
#js{} m))
|
||||
|
||||
(defn cljsify [body]
|
||||
@ -285,14 +289,17 @@
|
||||
(defn create-class
|
||||
"Creates JS class based on provided Clojure map.
|
||||
|
||||
Map keys should use `React.Component` method names (https://reactjs.org/docs/react-component.html).
|
||||
Map keys should use `React.Component` method names (https://reactjs.org/docs/react-component.html),
|
||||
and can be provided in snake-case or camelCase.
|
||||
Constructor function is defined using key `:getInitialState`.
|
||||
|
||||
React built-in static methods or properties are automatically defined as statics."
|
||||
[body]
|
||||
{:pre [(map? body)]}
|
||||
(let [body (cljsify body)
|
||||
methods (map-to-js (apply dissoc body :displayName :getInitialState built-in-static-method-names))
|
||||
methods (map-to-js (apply dissoc body :displayName :getInitialState
|
||||
:render :reagentRender
|
||||
built-in-static-method-names))
|
||||
static-methods (map-to-js (select-keys body built-in-static-method-names))
|
||||
display-name (:displayName body)
|
||||
construct (:getInitialState body)
|
||||
@ -302,7 +309,20 @@
|
||||
(when construct
|
||||
(construct this))
|
||||
this))]
|
||||
|
||||
(gobj/extend (.-prototype cmp) (.-prototype react/Component) methods)
|
||||
|
||||
;; These names SHOULD be mangled by Closure so we can't use goog/extend
|
||||
|
||||
(when (:render body)
|
||||
(set! (.-render (.-prototype cmp)) (:render body)))
|
||||
|
||||
(when (:reagentRender body)
|
||||
(set! (.-reagentRender (.-prototype cmp)) (:reagentRender body)))
|
||||
|
||||
(when (:cljsLegacyRender body)
|
||||
(set! (.-cljsLegacyRender (.-prototype cmp)) (:cljsLegacyRender body)))
|
||||
|
||||
(gobj/extend cmp react/Component static-methods)
|
||||
|
||||
(when display-name
|
||||
@ -319,10 +339,10 @@
|
||||
|
||||
(defn fiber-component-path [fiber]
|
||||
(let [name (some-> fiber
|
||||
($ :type)
|
||||
($ :displayName))
|
||||
(.-type)
|
||||
(.-displayName))
|
||||
parent (some-> fiber
|
||||
($ :return))
|
||||
(.-return))
|
||||
path (some-> parent
|
||||
fiber-component-path
|
||||
(str " > "))
|
||||
@ -332,18 +352,18 @@
|
||||
(defn component-path [c]
|
||||
;; Alternative branch for React 16
|
||||
;; Try both original name (for UMD foreign-lib) and manged name (property access, for Closure optimized React)
|
||||
(if-let [fiber (or (some-> c ($ :_reactInternalFiber))
|
||||
(if-let [fiber (or (some-> c (gobj/get "_reactInternalFiber"))
|
||||
(some-> c (.-_reactInternalFiber)))]
|
||||
(fiber-component-path fiber)
|
||||
(let [instance (or (some-> c ($ :_reactInternalInstance))
|
||||
(let [instance (or (some-> c (gobj/get "_reactInternalInstance"))
|
||||
(some-> c (.-_reactInternalInstance))
|
||||
c)
|
||||
elem (or (some-> instance ($ :_currentElement))
|
||||
elem (or (some-> instance (gobj/get "_currentElement"))
|
||||
(some-> instance (.-_currentElement)))
|
||||
name (some-> elem
|
||||
($ :type)
|
||||
($ :displayName))
|
||||
owner (or (some-> elem ($ :_owner))
|
||||
(.-type)
|
||||
(.-displayName))
|
||||
owner (or (some-> elem (gobj/get "_owner"))
|
||||
(some-> elem (.-_owner)))
|
||||
path (some-> owner
|
||||
component-path
|
||||
@ -367,8 +387,8 @@
|
||||
(not (reagent-class? f))))
|
||||
"Using native React classes directly in Hiccup forms "
|
||||
"is not supported. Use create-element or "
|
||||
"adapt-react-class instead: " (let [n (util/fun-name f)]
|
||||
(if (empty? n) f n))
|
||||
"adapt-react-class instead: " (or (util/fun-name f)
|
||||
f)
|
||||
(comp-name))
|
||||
(if (reagent-class? f)
|
||||
(cache-react-class f f)
|
||||
|
@ -6,9 +6,9 @@
|
||||
[reagent.impl.component :as comp]
|
||||
[reagent.impl.batching :as batch]
|
||||
[reagent.ratom :as ratom]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[reagent.debug :refer-macros [dbg prn println log dev?
|
||||
warn warn-unless]]))
|
||||
warn warn-unless]]
|
||||
[goog.object :as gobj]))
|
||||
|
||||
(declare as-element)
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
from a tag name."}
|
||||
re-tag #"([^\s\.#]+)(?:#([^\s\.#]+))?(?:\.([^\s#]+))?")
|
||||
|
||||
(deftype NativeWrapper [])
|
||||
(deftype NativeWrapper [tag id className])
|
||||
|
||||
|
||||
;;; Common utilities
|
||||
@ -40,14 +40,15 @@
|
||||
|
||||
(defn cache-get [o k]
|
||||
(when ^boolean (.hasOwnProperty o k)
|
||||
(aget o k)))
|
||||
(gobj/get o k)))
|
||||
|
||||
(defn cached-prop-name [k]
|
||||
(if (named? k)
|
||||
(if-some [k' (cache-get prop-name-cache (name k))]
|
||||
k'
|
||||
(aset prop-name-cache (name k)
|
||||
(util/dash-to-camel k)))
|
||||
(let [v (util/dash-to-camel k)]
|
||||
(gobj/set prop-name-cache (name k))
|
||||
v))
|
||||
k))
|
||||
|
||||
(defn ^boolean js-val? [x]
|
||||
@ -57,8 +58,7 @@
|
||||
|
||||
(defn kv-conv [o k v]
|
||||
(doto o
|
||||
(aset (cached-prop-name k)
|
||||
(convert-prop-value v))))
|
||||
(gobj/set (cached-prop-name k) (convert-prop-value v))))
|
||||
|
||||
(defn convert-prop-value [x]
|
||||
(cond (js-val? x) x
|
||||
@ -78,14 +78,14 @@
|
||||
(if (named? k)
|
||||
(if-some [k' (cache-get custom-prop-name-cache (name k))]
|
||||
k'
|
||||
(aset custom-prop-name-cache (name k)
|
||||
(util/dash-to-camel k)))
|
||||
(let [v (util/dash-to-camel k)]
|
||||
(gobj/set custom-prop-name-cache (name k) v)
|
||||
v))
|
||||
k))
|
||||
|
||||
(defn custom-kv-conv [o k v]
|
||||
(doto o
|
||||
(aset (cached-custom-prop-name k)
|
||||
(convert-prop-value v))))
|
||||
(gobj/set (cached-custom-prop-name k) (convert-prop-value v))))
|
||||
|
||||
(defn convert-custom-prop-value [x]
|
||||
(cond (js-val? x) x
|
||||
@ -96,19 +96,12 @@
|
||||
(apply x args))
|
||||
:else (clj->js x)))
|
||||
|
||||
(defn oset [o k v]
|
||||
(doto (if (nil? o) #js{} o)
|
||||
(aset k v)))
|
||||
|
||||
(defn oget [o k]
|
||||
(if (nil? o) nil (aget o k)))
|
||||
|
||||
(defn set-id-class
|
||||
"Takes the id and class from tag keyword, and adds them to the
|
||||
other props. Parsed tag is JS object with :id and :class properties."
|
||||
[props id-class]
|
||||
(let [id ($ id-class :id)
|
||||
class ($ id-class :class)]
|
||||
(let [id (.-id id-class)
|
||||
class (.-className id-class)]
|
||||
(cond-> props
|
||||
;; Only use ID from tag keyword if no :id in props already
|
||||
(and (some? id)
|
||||
@ -124,7 +117,7 @@
|
||||
props (-> props
|
||||
(cond-> class (assoc :class (util/class-names class)))
|
||||
(set-id-class id-class))]
|
||||
(if ($ id-class :custom)
|
||||
(if (.-custom id-class)
|
||||
(convert-custom-prop-value props)
|
||||
(convert-prop-value props))))
|
||||
|
||||
@ -147,14 +140,14 @@
|
||||
|
||||
(defn input-node-set-value
|
||||
[node rendered-value dom-value component {:keys [on-write]}]
|
||||
(if-not (and (identical? node ($ js/document :activeElement))
|
||||
(has-selection-api? ($ node :type))
|
||||
(if-not (and (identical? node (.-activeElement js/document))
|
||||
(has-selection-api? (.-type node))
|
||||
(string? rendered-value)
|
||||
(string? dom-value))
|
||||
;; just set the value, no need to worry about a cursor
|
||||
(do
|
||||
($! component :cljsDOMValue rendered-value)
|
||||
($! node :value rendered-value)
|
||||
(set! (.-cljsDOMValue component) rendered-value)
|
||||
(set! (.-value node) rendered-value)
|
||||
(when (fn? on-write)
|
||||
(on-write rendered-value)))
|
||||
|
||||
@ -179,37 +172,37 @@
|
||||
;; So this is just a warning. The code below is simple
|
||||
;; enough, but if you are tempted to change it, be aware of
|
||||
;; all the scenarios you have handle.
|
||||
(let [node-value ($ node :value)]
|
||||
(let [node-value (.-value node)]
|
||||
(if (not= node-value dom-value)
|
||||
;; IE has not notified us of the change yet, so check again later
|
||||
(batch/do-after-render #(input-component-set-value component))
|
||||
(let [existing-offset-from-end (- (count node-value)
|
||||
($ node :selectionStart))
|
||||
(.-selectionStart node))
|
||||
new-cursor-offset (- (count rendered-value)
|
||||
existing-offset-from-end)]
|
||||
($! component :cljsDOMValue rendered-value)
|
||||
($! node :value rendered-value)
|
||||
(set! (.-cljsDOMValue component) rendered-value)
|
||||
(set! (.-value node) rendered-value)
|
||||
(when (fn? on-write)
|
||||
(on-write rendered-value))
|
||||
($! node :selectionStart new-cursor-offset)
|
||||
($! node :selectionEnd new-cursor-offset))))))
|
||||
(set! (.-selectionStart node) new-cursor-offset)
|
||||
(set! (.-selectionEnd node) new-cursor-offset))))))
|
||||
|
||||
(defn input-component-set-value [this]
|
||||
(when ($ this :cljsInputLive)
|
||||
($! this :cljsInputDirty false)
|
||||
(let [rendered-value ($ this :cljsRenderedValue)
|
||||
dom-value ($ this :cljsDOMValue)
|
||||
(when (.-cljsInputLive this)
|
||||
(set! (.-cljsInputDirty this) false)
|
||||
(let [rendered-value (.-cljsRenderedValue this)
|
||||
dom-value (.-cljsDOMValue this)
|
||||
;; Default to the root node within this component
|
||||
node (find-dom-node this)]
|
||||
(when (not= rendered-value dom-value)
|
||||
(input-node-set-value node rendered-value dom-value this {})))))
|
||||
|
||||
(defn input-handle-change [this on-change e]
|
||||
($! this :cljsDOMValue (-> e .-target .-value))
|
||||
(set! (.-cljsDOMValue this) (-> e .-target .-value))
|
||||
;; Make sure the input is re-rendered, in case on-change
|
||||
;; wants to keep the value unchanged
|
||||
(when-not ($ this :cljsInputDirty)
|
||||
($! this :cljsInputDirty true)
|
||||
(when-not (.-cljsInputDirty this)
|
||||
(set! (.-cljsInputDirty this) true)
|
||||
(batch/do-after-render #(input-component-set-value this)))
|
||||
(on-change e))
|
||||
|
||||
@ -222,21 +215,20 @@
|
||||
(.hasOwnProperty jsprops "value"))
|
||||
(assert find-dom-node
|
||||
"reagent.dom needs to be loaded for controlled input to work")
|
||||
(let [v ($ jsprops :value)
|
||||
(let [v (.-value jsprops)
|
||||
value (if (nil? v) "" v)
|
||||
on-change ($ jsprops :onChange)]
|
||||
(when-not ($ this :cljsInputLive)
|
||||
on-change (.-onChange jsprops)]
|
||||
(when-not (.-cljsInputLive this)
|
||||
;; set initial value
|
||||
($! this :cljsInputLive true)
|
||||
($! this :cljsDOMValue value))
|
||||
($! this :cljsRenderedValue value)
|
||||
(set! (.-cljsInputLive this) true)
|
||||
(set! (.-cljsDOMValue this) value))
|
||||
(set! (.-cljsRenderedValue this) value)
|
||||
(js-delete jsprops "value")
|
||||
(doto jsprops
|
||||
($! :defaultValue value)
|
||||
($! :onChange #(input-handle-change this on-change %))))))
|
||||
(set! (.-defaultValue jsprops) value)
|
||||
(set! (.-onChange jsprops) #(input-handle-change this on-change %)))))
|
||||
|
||||
(defn input-unmount [this]
|
||||
($! this :cljsInputLive nil))
|
||||
(set! (.-cljsInputLive this) nil))
|
||||
|
||||
(defn ^boolean input-component? [x]
|
||||
(case x
|
||||
@ -252,10 +244,10 @@
|
||||
:component-did-update input-component-set-value
|
||||
:component-will-unmount input-unmount
|
||||
:reagent-render
|
||||
(fn [argv comp jsprops first-child]
|
||||
(fn [argv component jsprops first-child]
|
||||
(let [this comp/*current-component*]
|
||||
(input-render-setup this jsprops)
|
||||
(make-element argv comp jsprops first-child)))})
|
||||
(make-element argv component jsprops first-child)))})
|
||||
|
||||
(defn reagent-input
|
||||
[]
|
||||
@ -266,18 +258,19 @@
|
||||
|
||||
;;; Conversion from Hiccup forms
|
||||
|
||||
(deftype HiccupTag [tag id className custom])
|
||||
|
||||
(defn parse-tag [hiccup-tag]
|
||||
(let [[tag id class] (->> hiccup-tag name (re-matches re-tag) next)
|
||||
class (when-not (nil? class)
|
||||
(string/replace class #"\." " "))]
|
||||
(assert tag (str "Invalid tag: '" hiccup-tag "'"
|
||||
(comp/comp-name)))
|
||||
#js {:name tag
|
||||
:id id
|
||||
:class class
|
||||
;; Custom element names must contain hyphen
|
||||
;; https://www.w3.org/TR/custom-elements/#custom-elements-core-concepts
|
||||
:custom (not= -1 (.indexOf tag "-"))}))
|
||||
(let [[tag id className] (->> hiccup-tag name (re-matches re-tag) next)
|
||||
className (when-not (nil? className)
|
||||
(string/replace className #"\." " "))]
|
||||
(assert tag (str "Invalid tag: '" hiccup-tag "'" (comp/comp-name)))
|
||||
(->HiccupTag tag
|
||||
id
|
||||
className
|
||||
;; Custom element names must contain hyphen
|
||||
;; https://www.w3.org/TR/custom-elements/#custom-elements-core-concepts
|
||||
(not= -1 (.indexOf tag "-")))))
|
||||
|
||||
(defn try-get-key [x]
|
||||
;; try catch to avoid clojurescript peculiarity with
|
||||
@ -296,50 +289,50 @@
|
||||
|
||||
(defn reag-element [tag v]
|
||||
(let [c (comp/as-class tag)
|
||||
jsprops #js{:argv v}]
|
||||
jsprops #js {}]
|
||||
(set! (.-argv jsprops) v)
|
||||
(when-some [key (key-from-vec v)]
|
||||
($! jsprops :key key))
|
||||
(set! (.-key jsprops) key))
|
||||
(react/createElement c jsprops)))
|
||||
|
||||
(defn fragment-element [argv]
|
||||
(let [props (nth argv 1 nil)
|
||||
hasprops (or (nil? props) (map? props))
|
||||
jsprops (convert-prop-value (if hasprops props))
|
||||
jsprops (if-some [key (key-from-vec argv)]
|
||||
(oset jsprops "key" key)
|
||||
jsprops)
|
||||
jsprops (or (convert-prop-value (if hasprops props))
|
||||
#js {})
|
||||
first-child (+ 1 (if hasprops 1 0))]
|
||||
(when-some [key (key-from-vec argv)]
|
||||
(set! (.-key jsprops) key))
|
||||
(make-element argv react/Fragment jsprops first-child)))
|
||||
|
||||
(defn adapt-react-class
|
||||
[c]
|
||||
(doto (->NativeWrapper)
|
||||
($! :name c)
|
||||
($! :id nil)
|
||||
($! :class nil)))
|
||||
(->NativeWrapper c nil nil))
|
||||
|
||||
(def tag-name-cache #js{})
|
||||
|
||||
(defn cached-parse [x]
|
||||
(if-some [s (cache-get tag-name-cache x)]
|
||||
s
|
||||
(aset tag-name-cache x (parse-tag x))))
|
||||
(let [v (parse-tag x)]
|
||||
(gobj/set tag-name-cache x v)
|
||||
v)))
|
||||
|
||||
(defn native-element [parsed argv first]
|
||||
(let [comp ($ parsed :name)
|
||||
(let [component (.-tag parsed)
|
||||
props (nth argv first nil)
|
||||
hasprops (or (nil? props) (map? props))
|
||||
jsprops (convert-props (if hasprops props) parsed)
|
||||
jsprops (or (convert-props (if hasprops props) parsed)
|
||||
#js {})
|
||||
first-child (+ first (if hasprops 1 0))]
|
||||
(if (input-component? comp)
|
||||
(-> [(reagent-input) argv comp jsprops first-child]
|
||||
(if (input-component? component)
|
||||
(-> [(reagent-input) argv component jsprops first-child]
|
||||
(with-meta (meta argv))
|
||||
as-element)
|
||||
(let [key (-> (meta argv) get-key)
|
||||
p (if (nil? key)
|
||||
jsprops
|
||||
(oset jsprops "key" key))]
|
||||
(make-element argv comp p first-child)))))
|
||||
(do
|
||||
(when-some [key (-> (meta argv) get-key)]
|
||||
(set! (.-key jsprops) key))
|
||||
(make-element argv component jsprops first-child)))))
|
||||
|
||||
(defn str-coll [coll]
|
||||
(if (dev?)
|
||||
@ -366,11 +359,10 @@
|
||||
pos (.indexOf n ">")]
|
||||
(case pos
|
||||
-1 (native-element (cached-parse n) v 1)
|
||||
;; TODO: Doesn't this match :>foo or any keyword starting with >
|
||||
0 (let [comp (nth v 1 nil)]
|
||||
;; Support [:> comp ...]
|
||||
0 (let [component (nth v 1 nil)]
|
||||
;; Support [:> component ...]
|
||||
(assert (= ">" n) (hiccup-err v "Invalid Hiccup tag"))
|
||||
(native-element #js{:name comp} v 2))
|
||||
(native-element (->HiccupTag component nil nil nil) v 2))
|
||||
;; Support extended hiccup syntax, i.e :div.bar>a.foo
|
||||
;; Apply metadata (e.g. :key) to the outermost element.
|
||||
;; Metadata is probably used only with sequeneces, and in that case
|
||||
@ -400,20 +392,15 @@
|
||||
(set! comp/as-element as-element)
|
||||
|
||||
(defn expand-seq [s]
|
||||
(let [a (into-array s)]
|
||||
(dotimes [i (alength a)]
|
||||
(aset a i (as-element (aget a i))))
|
||||
a))
|
||||
(into-array (map as-element s)))
|
||||
|
||||
(defn expand-seq-dev [s o]
|
||||
(let [a (into-array s)]
|
||||
(dotimes [i (alength a)]
|
||||
(let [val (aget a i)]
|
||||
(when (and (vector? val)
|
||||
(nil? (key-from-vec val)))
|
||||
($! o :no-key true))
|
||||
(aset a i (as-element val))))
|
||||
a))
|
||||
(into-array (map (fn [val]
|
||||
(when (and (vector? val)
|
||||
(nil? (key-from-vec val)))
|
||||
(set! (.-no-key o) true))
|
||||
(as-element val))
|
||||
s)))
|
||||
|
||||
(defn expand-seq-check [x]
|
||||
(let [ctx #js{}
|
||||
@ -421,7 +408,7 @@
|
||||
(when derefed
|
||||
(warn (hiccup-err x "Reactive deref not supported in lazy seq, "
|
||||
"it should be wrapped in doall")))
|
||||
(when ($ ctx :no-key)
|
||||
(when (.-no-key ctx)
|
||||
(warn (hiccup-err x "Every element in a seq should have a unique :key")))
|
||||
res))
|
||||
|
||||
@ -452,12 +439,12 @@
|
||||
;; ;; "_store" (js-obj)
|
||||
;; )))
|
||||
|
||||
(defn make-element [argv comp jsprops first-child]
|
||||
(defn make-element [argv component jsprops first-child]
|
||||
(case (- (count argv) first-child)
|
||||
;; Optimize cases of zero or one child
|
||||
0 (react/createElement comp jsprops)
|
||||
0 (react/createElement component jsprops)
|
||||
|
||||
1 (react/createElement comp jsprops
|
||||
1 (react/createElement component jsprops
|
||||
(as-element (nth argv first-child nil)))
|
||||
|
||||
(.apply react/createElement nil
|
||||
@ -465,4 +452,4 @@
|
||||
(when (>= k first-child)
|
||||
(.push a (as-element v)))
|
||||
a)
|
||||
#js[comp jsprops] argv))))
|
||||
#js[component jsprops] argv))))
|
||||
|
@ -1,10 +1,9 @@
|
||||
(ns reagent.impl.util
|
||||
(:require [reagent.debug :refer-macros [dbg log warn]]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[clojure.string :as string]))
|
||||
|
||||
(def is-client (and (exists? js/window)
|
||||
(-> js/window ($ :document) nil? not)))
|
||||
(-> (.-document js/window) nil? not)))
|
||||
|
||||
(def ^:dynamic ^boolean *non-reactive* false)
|
||||
|
||||
@ -40,16 +39,17 @@
|
||||
|
||||
(defn fun-name [f]
|
||||
(let [n (or (and (fn? f)
|
||||
(or ($ f :displayName)
|
||||
($ f :name)))
|
||||
(or (.-displayName f)
|
||||
(let [n (.-name f)]
|
||||
(if (and (string? n) (seq n))
|
||||
n))))
|
||||
(and (implements? INamed f)
|
||||
(name f))
|
||||
(let [m (meta f)]
|
||||
(if (map? m)
|
||||
(:name m))))]
|
||||
(-> n
|
||||
str
|
||||
(clojure.string/replace "$" "."))))
|
||||
(if n
|
||||
(string/replace (str n) "$" "."))))
|
||||
|
||||
(deftype PartialFn [pfn f args]
|
||||
Fn
|
||||
@ -174,5 +174,5 @@
|
||||
(defn force-update [comp deep]
|
||||
(if deep
|
||||
(binding [*always-update* true]
|
||||
($ comp forceUpdate))
|
||||
($ comp forceUpdate)))
|
||||
(.forceUpdate comp))
|
||||
(.forceUpdate comp)))
|
||||
|
@ -1,56 +0,0 @@
|
||||
(ns reagent.interop
|
||||
(:require [clojure.string :as string :refer [join]]))
|
||||
|
||||
(defn- js-call [f args]
|
||||
(let [argstr (->> (repeat (count args) "~{}")
|
||||
(join ","))]
|
||||
(list* 'js* (str "~{}(" argstr ")") f args)))
|
||||
|
||||
(defn- dot-args [object member]
|
||||
(assert (or (symbol? member)
|
||||
(keyword? member))
|
||||
(str "Symbol or keyword expected, not " (pr-str member)))
|
||||
(assert (or (not (symbol? object))
|
||||
(not (re-find #"\." (name object))))
|
||||
(str "Dot not allowed in " object))
|
||||
(let [n (name member)
|
||||
field? (or (keyword? member)
|
||||
(= (subs n 0 1) "-"))
|
||||
names (-> (if (symbol? member)
|
||||
(string/replace n #"^-" "")
|
||||
n)
|
||||
(string/split #"\."))]
|
||||
[field? names]))
|
||||
|
||||
(defmacro $
|
||||
"Access member in a javascript object, in a Closure-safe way.
|
||||
'member' is assumed to be a field if it is a keyword or if
|
||||
the name starts with '-', otherwise the named function is
|
||||
called with the optional args.
|
||||
'member' may contain '.', to allow access in nested objects.
|
||||
If 'object' is a symbol it is not allowed contain '.'.
|
||||
|
||||
($ o :foo) is equivalent to (.-foo o), except that it gives
|
||||
the same result under advanced compilation.
|
||||
($ o foo arg1 arg2) is the same as (.foo o arg1 arg2)."
|
||||
[object member & args]
|
||||
(let [[field names] (dot-args object member)]
|
||||
(if field
|
||||
(do
|
||||
(assert (empty? args)
|
||||
(str "Passing args to field doesn't make sense: " member))
|
||||
`(aget ~object ~@names))
|
||||
(js-call (list* 'aget object names) args))))
|
||||
|
||||
(defmacro $!
|
||||
"Set field in a javascript object, in a Closure-safe way.
|
||||
'field' should be a keyword or a symbol starting with '-'.
|
||||
'field' may contain '.', to allow access in nested objects.
|
||||
If 'object' is a symbol it is not allowed contain '.'.
|
||||
|
||||
($! o :foo 1) is equivalent to (set! (.-foo o) 1), except that it
|
||||
gives the same result under advanced compilation."
|
||||
[object field value]
|
||||
(let [[field names] (dot-args object field)]
|
||||
(assert field (str "Field name must start with - in " field))
|
||||
`(aset ~object ~@names ~value)))
|
@ -1,2 +0,0 @@
|
||||
(ns reagent.interop
|
||||
(:require-macros [reagent.interop]))
|
@ -14,6 +14,25 @@
|
||||
(deref co#)
|
||||
co#))
|
||||
|
||||
; taken from cljs.core
|
||||
; https://github.com/binaryage/cljs-oops/issues/14
|
||||
(defmacro unchecked-aget
|
||||
([array idx]
|
||||
(list 'js* "(~{}[~{}])" array idx))
|
||||
([array idx & idxs]
|
||||
(let [astr (apply str (repeat (count idxs) "[~{}]"))]
|
||||
`(~'js* ~(str "(~{}[~{}]" astr ")") ~array ~idx ~@idxs))))
|
||||
|
||||
; taken from cljs.core
|
||||
; https://github.com/binaryage/cljs-oops/issues/14
|
||||
(defmacro unchecked-aset
|
||||
([array idx val]
|
||||
(list 'js* "(~{}[~{}] = ~{})" array idx val))
|
||||
([array idx idx2 & idxv]
|
||||
(let [n (dec (count idxv))
|
||||
astr (apply str (repeat n "[~{}]"))]
|
||||
`(~'js* ~(str "(~{}[~{}][~{}]" astr " = ~{})") ~array ~idx ~idx2 ~@idxv))))
|
||||
|
||||
(defmacro with-let [bindings & body]
|
||||
(assert (vector? bindings)
|
||||
(str "with-let bindings must be a vector, not "
|
||||
@ -27,8 +46,8 @@
|
||||
x
|
||||
(let [j (quot i 2)]
|
||||
`(if ~init
|
||||
(aset ~v ~j ~x)
|
||||
(aget ~v ~j)))))
|
||||
(unchecked-aset ~v ~j ~x)
|
||||
(unchecked-aget ~v ~j)))))
|
||||
bindings))
|
||||
[forms destroy] (let [fin (last body)]
|
||||
(if (and (list? fin)
|
||||
|
@ -4,7 +4,8 @@
|
||||
(:require [reagent.impl.util :as util]
|
||||
[reagent.debug :refer-macros [dbg log warn error dev? time]]
|
||||
[reagent.impl.batching :as batch]
|
||||
[clojure.set :as s]))
|
||||
[clojure.set :as s]
|
||||
[goog.object :as obj]))
|
||||
|
||||
(declare ^:dynamic *ratom-context*)
|
||||
(defonce ^boolean debug false)
|
||||
@ -187,10 +188,8 @@
|
||||
|
||||
(declare make-reaction)
|
||||
|
||||
(def ^{:private true :const true} cache-key "reagReactionCache")
|
||||
|
||||
(defn- cached-reaction [f o k obj destroy]
|
||||
(let [m (aget o cache-key)
|
||||
(let [m (.-reagReactionCache o)
|
||||
m (if (nil? m) {} m)
|
||||
r (m k nil)]
|
||||
(cond
|
||||
@ -199,15 +198,15 @@
|
||||
:else (let [r (make-reaction
|
||||
f :on-dispose (fn [x]
|
||||
(when debug (swap! -running dec))
|
||||
(as-> (aget o cache-key) _
|
||||
(as-> (.-reagReactionCache o) _
|
||||
(dissoc _ k)
|
||||
(aset o cache-key _))
|
||||
(set! (.-reagReactionCache o) _))
|
||||
(when (some? obj)
|
||||
(set! (.-reaction obj) nil))
|
||||
(when (some? destroy)
|
||||
(destroy x))))
|
||||
v (-deref r)]
|
||||
(aset o cache-key (assoc m k r))
|
||||
(set! (.-reagReactionCache o) (assoc m k r))
|
||||
(when debug (swap! -running inc))
|
||||
(when (some? obj)
|
||||
(set! (.-reaction obj) r))
|
||||
@ -538,7 +537,7 @@
|
||||
(._set-opts r opts)
|
||||
(set! (.-f r) f)
|
||||
(set! (.-auto-run r) #(run obj))
|
||||
(aset obj key r))
|
||||
(obj/set obj key r))
|
||||
res))
|
||||
|
||||
(defn check-derefs [f]
|
||||
|
@ -11,10 +11,14 @@
|
||||
(is (= "class"
|
||||
(tmpl/cached-custom-prop-name :class))))
|
||||
|
||||
;; Cljs.test prints better error if check is Cljs function
|
||||
(defn js-equal? [a b]
|
||||
(gobj/equals a b))
|
||||
|
||||
(deftest convert-props-test
|
||||
(is (gobj/equals #js {:className "a"}
|
||||
(tmpl/convert-props {:class "a"} #js {:id nil :custom false})))
|
||||
(is (gobj/equals #js {:class "a"}
|
||||
(tmpl/convert-props {:class "a"} #js {:id nil :custom true})))
|
||||
(is (gobj/equals #js {:className "a b" :id "a"}
|
||||
(tmpl/convert-props {:class "b"} #js {:id "a" :class "a" :custom false}))))
|
||||
(is (js-equal? #js {:className "a"}
|
||||
(tmpl/convert-props {:class "a"} (tmpl/HiccupTag. nil nil nil false))))
|
||||
(is (js-equal? #js {:class "a"}
|
||||
(tmpl/convert-props {:class "a"} (tmpl/HiccupTag. nil nil nil true))))
|
||||
(is (js-equal? #js {:className "a b" :id "a"}
|
||||
(tmpl/convert-props {:class "b"} (tmpl/HiccupTag. nil "a" "a" false)))))
|
||||
|
@ -1,7 +1,6 @@
|
||||
(ns reagenttest.runtests
|
||||
(:require [reagenttest.testreagent]
|
||||
[reagenttest.testcursor]
|
||||
[reagenttest.testinterop]
|
||||
[reagenttest.testratom]
|
||||
[reagenttest.testratomasync]
|
||||
[reagenttest.testtrack]
|
||||
|
@ -1,56 +0,0 @@
|
||||
(ns reagenttest.testinterop
|
||||
(:require [cljs.test :as t :refer-macros [is deftest]]
|
||||
[reagent.debug :refer-macros [dbg]]
|
||||
[reagent.interop :refer-macros [$ $!]]))
|
||||
|
||||
;; (def is-adv (let [o #js{}]
|
||||
;; (set! (.-somethinglong o) true)
|
||||
;; (not= (aget (.keys js/Object o) 0) "somethinglong")))
|
||||
|
||||
(deftest iterop-quote
|
||||
(let [o #js{:foo "foo"
|
||||
:foobar #js{:bar "bar"
|
||||
:bar-foo "bar-foo"}
|
||||
:bar-foo "barfoo"}]
|
||||
|
||||
(is (= "foo" ($ o :foo)))
|
||||
(is (= "bar" ($ o :foobar.bar)))
|
||||
(is (= "barfoo" ($ o :bar-foo)))
|
||||
|
||||
(is (= "foo" ($ o -foo)))
|
||||
(is (= "bar" ($ o -foobar.bar)))
|
||||
(is (= "bar-foo" ($ o -foobar.bar-foo)))
|
||||
(is (= "bar-foo" ($ o :foobar.bar-foo)))
|
||||
|
||||
($! o :foo "foo1")
|
||||
(is (= "foo1" ($ o :foo)))
|
||||
|
||||
($! o -foo "foo2")
|
||||
(is (= "foo2" ($ o -foo)))
|
||||
|
||||
($! o :foobar.bar "bar1")
|
||||
(is (= "bar1" ($ o :foobar.bar)))
|
||||
|
||||
($! o -foobar.bar "bar1")
|
||||
(is (= "bar1" ($ o -foobar.bar)))))
|
||||
|
||||
(deftest interop-quote-call
|
||||
(let [o #js{:bar "bar1"
|
||||
:foo (fn [x]
|
||||
(this-as this
|
||||
(str x ($ this :bar))))}
|
||||
o2 #js{:o o}]
|
||||
(is (= "ybar1" ($ o foo "y")))
|
||||
(is (= "xxbar1" ($ o2 o.foo "xx")))
|
||||
(is (= "abar1" (-> o2
|
||||
($ :o)
|
||||
($ foo "a"))))
|
||||
|
||||
(is (= "bar1" ($ o foo)))
|
||||
(is (= "bar1" ($ o2 o.foo)))
|
||||
|
||||
($! o :bar "bar2")
|
||||
(is (= "bar2" ($ o foo)))
|
||||
|
||||
(is (= "1bar2" ($ ($ o :foo)
|
||||
call o 1)))))
|
@ -3,7 +3,6 @@
|
||||
[react :as react]
|
||||
[reagent.ratom :as rv :refer-macros [reaction]]
|
||||
[reagent.debug :as debug :refer-macros [dbg println log dev?]]
|
||||
[reagent.interop :refer-macros [$ $!]]
|
||||
[reagent.core :as r]
|
||||
[reagent.dom.server :as server]
|
||||
[reagent.impl.util :as util]
|
||||
@ -396,7 +395,7 @@
|
||||
(swap! top-ran inc)
|
||||
(r/create-class
|
||||
{:component-did-mount #(swap! ran inc)
|
||||
:component-function
|
||||
:reagent-render
|
||||
(fn [p a]
|
||||
(is (= 1 a))
|
||||
(swap! ran inc)
|
||||
@ -448,8 +447,8 @@
|
||||
(this-as
|
||||
this
|
||||
(r/create-element
|
||||
"div" #js {:className ($ this :props.className)}
|
||||
($ this :props.children))))})
|
||||
"div" #js {:className (.. this -props -className)}
|
||||
(.. this -props -children))))})
|
||||
(gobj/extend cmp react/Component)
|
||||
cmp))
|
||||
|
||||
@ -1094,7 +1093,7 @@
|
||||
(let [old @spy]
|
||||
(is (nil? (r/after-render
|
||||
(fn []
|
||||
(is (= "DIV" ($ @node :tagName)))
|
||||
(is (= "DIV" (.-tagName @node)))
|
||||
(swap! spy inc)))))
|
||||
(is (= old @spy))
|
||||
(is (= @exp @val))
|
||||
|
Loading…
x
Reference in New Issue
Block a user