mirror of https://github.com/status-im/reagent.git
Breaking change: Allow arbitrary arguments to component functions
Previously, the first argument had to be a map, and the rest of the arguments where passed as a vector as the second argument. Now [my-comp foo...] generally behaves as (my-comp foo...). Since `this` is no longer passed to component functions, add current-component function. Also change signatures of React callbacks, to reflect that arbitrary arguments can be passed. Remove set-props and replace-props for the same reason. Add argv accessor and set-args instead.
This commit is contained in:
parent
69384d98c6
commit
3c2c63402e
|
@ -1,5 +1,5 @@
|
|||
|
||||
(defproject reagent "0.3.0"
|
||||
(defproject reagent "0.4.0-SNAPSHOT"
|
||||
:url "http://github.com/holmsand/reagent"
|
||||
:license {:name "MIT"}
|
||||
:description "A simple ClojureScript interface to React"
|
||||
|
|
|
@ -41,14 +41,14 @@ Returns the mounted component instance."
|
|||
"Create a component, React style. Should be called with a map,
|
||||
looking like this:
|
||||
{:get-initial-state (fn [this])
|
||||
:component-will-receive-props (fn [this new-props])
|
||||
:should-component-update (fn [this old-props new-props old-children new-children])
|
||||
:component-will-receive-props (fn [this new-argv])
|
||||
:should-component-update (fn [this old-argv new-argv])
|
||||
:component-will-mount (fn [this])
|
||||
:component-did-mount (fn [this])
|
||||
:component-will-update (fn [this new-props new-children])
|
||||
:component-did-update (fn [this old-props old-children])
|
||||
:component-will-update (fn [this new-argv])
|
||||
:component-did-update (fn [this old-argv])
|
||||
:component-will-unmount (fn [this])
|
||||
:render (fn [props children this])}
|
||||
:render (fn [this])}
|
||||
|
||||
Everything is optional, except :render.
|
||||
"
|
||||
|
@ -56,16 +56,12 @@ Everything is optional, except :render.
|
|||
(comp/create-class spec))
|
||||
|
||||
|
||||
(defn replace-args [comp new-args]
|
||||
(assert (vector? new-args))
|
||||
(comp/set-args comp new-args))
|
||||
|
||||
(defn set-props
|
||||
"Merge the props of a mounted, top-level component."
|
||||
[comp props]
|
||||
(comp/set-props comp props))
|
||||
|
||||
(defn replace-props
|
||||
"Set the props of a mounted, top-level component."
|
||||
[comp props]
|
||||
(comp/replace-props comp props))
|
||||
(defn current-component []
|
||||
comp/*current-component*)
|
||||
|
||||
|
||||
(defn state
|
||||
|
@ -94,6 +90,11 @@ Everything is optional, except :render.
|
|||
[this]
|
||||
(comp/get-children this))
|
||||
|
||||
(defn argv
|
||||
"Returns the entire Hiccup form passed to the component."
|
||||
[this]
|
||||
(comp/get-argv this))
|
||||
|
||||
(defn dom-node
|
||||
"Returns the root DOM node of a mounted component."
|
||||
[this]
|
||||
|
|
|
@ -2,11 +2,12 @@
|
|||
(ns reagent.impl.component
|
||||
(:refer-clojure :exclude [flush])
|
||||
(:require [reagent.impl.template :as tmpl
|
||||
:refer [cljs-props cljs-children cljs-level React]]
|
||||
:refer [cljs-argv cljs-level React]]
|
||||
[reagent.impl.util :as util]
|
||||
[reagent.ratom :as ratom]
|
||||
[reagent.debug :refer-macros [dbg prn]]))
|
||||
|
||||
(declare ^:dynamic *current-component*)
|
||||
|
||||
(def cljs-state "cljsState")
|
||||
|
||||
|
@ -29,20 +30,29 @@
|
|||
(defn js-props [C]
|
||||
(aget C "props"))
|
||||
|
||||
(defn props-in-props [props]
|
||||
(aget props cljs-props))
|
||||
(defn extract-props [v]
|
||||
(let [p (get v 1)]
|
||||
(if (map? p) p)))
|
||||
|
||||
(defn extract-children [v]
|
||||
(let [first-child (if (-> v (get 1) map?) 2 1)]
|
||||
(if (> (count v) first-child)
|
||||
(subvec v first-child))))
|
||||
|
||||
(defn get-argv [C]
|
||||
(-> C js-props (aget cljs-argv)))
|
||||
|
||||
(defn get-props [C]
|
||||
(-> C js-props props-in-props))
|
||||
(-> C get-argv extract-props))
|
||||
|
||||
(defn get-children [C]
|
||||
(-> C js-props (aget cljs-children)))
|
||||
(-> C get-argv extract-children))
|
||||
|
||||
(defn replace-props [C newprops]
|
||||
(.setProps C (js-obj cljs-props newprops)))
|
||||
(defn set-args [C new-args]
|
||||
(let [argv (get-argv C)]
|
||||
(.setProps C (js-obj cljs-argv
|
||||
(into [(argv 0)] new-args)))))
|
||||
|
||||
(defn set-props [C newprops]
|
||||
(replace-props C (merge (get-props C) newprops)))
|
||||
|
||||
;;; Rendering
|
||||
|
||||
|
@ -98,17 +108,25 @@
|
|||
|
||||
(defn do-render [C f]
|
||||
(set! (.-cljsIsDirty C) false)
|
||||
(binding [*current-component* C]
|
||||
(let [p (js-props C)
|
||||
props (props-in-props p)
|
||||
children (aget p cljs-children)
|
||||
;; Call render function with props, children, component
|
||||
res (f props children C)
|
||||
argv (aget p cljs-argv)
|
||||
n (count argv)
|
||||
res (if (nil? (aget C "componentFunction"))
|
||||
(f C)
|
||||
(case n
|
||||
1 (f)
|
||||
2 (f (argv 1))
|
||||
3 (f (argv 1) (argv 2))
|
||||
4 (f (argv 1) (argv 2) (argv 3))
|
||||
5 (f (argv 1) (argv 2) (argv 3) (argv 4))
|
||||
(apply f (subvec argv 1))))
|
||||
conv (if (vector? res)
|
||||
(tmpl/as-component res (aget p cljs-level))
|
||||
(if (fn? res)
|
||||
(do-render C (set! (.-cljsRenderFn C) res))
|
||||
res))]
|
||||
conv))
|
||||
conv)))
|
||||
|
||||
(defn render [C]
|
||||
(assert C)
|
||||
|
@ -136,33 +154,28 @@
|
|||
|
||||
:componentWillReceiveProps
|
||||
(fn [C props]
|
||||
(when f (f C (props-in-props props))))
|
||||
(when f (f C (aget props cljs-argv))))
|
||||
|
||||
:shouldComponentUpdate
|
||||
(fn [C nextprops nextstate]
|
||||
;; Don't care about nextstate here, we use forceUpdate
|
||||
;; when only when state has changed anyway.
|
||||
(let [inprops (js-props C)
|
||||
p1 (aget inprops cljs-props)
|
||||
c1 (aget inprops cljs-children)
|
||||
p2 (aget nextprops cljs-props)
|
||||
c2 (aget nextprops cljs-children)]
|
||||
old-argv (aget inprops cljs-argv)
|
||||
new-argv (aget nextprops cljs-argv)]
|
||||
(if (nil? f)
|
||||
(not (util/equal-args p1 c1 p2 c2))
|
||||
;; call f with oldprops newprops oldchildren newchildren
|
||||
(f C p1 p2 c1 c2))))
|
||||
(not (util/equal-args old-argv new-argv))
|
||||
(f C old-argv new-argv))))
|
||||
|
||||
:componentWillUpdate
|
||||
(fn [C nextprops]
|
||||
(let [p (aget nextprops cljs-props)
|
||||
c (aget nextprops cljs-children)]
|
||||
(f C p c)))
|
||||
(let [next-argv (aget nextprops cljs-argv)]
|
||||
(f C next-argv)))
|
||||
|
||||
:componentDidUpdate
|
||||
(fn [C oldprops]
|
||||
(let [p (aget oldprops cljs-props)
|
||||
c (aget oldprops cljs-children)]
|
||||
(f C p c)))
|
||||
(let [old-argv (aget oldprops cljs-argv)]
|
||||
(f C old-argv)))
|
||||
|
||||
:componentWillUnmount
|
||||
(fn [C]
|
||||
|
@ -202,11 +215,15 @@
|
|||
|
||||
(defn wrap-funs [fun-map]
|
||||
(let [name (or (:displayName fun-map)
|
||||
(when-let [r (:render fun-map)]
|
||||
(when-let [r (or (:componentFunction fun-map)
|
||||
(:render fun-map))]
|
||||
(or (.-displayName r)
|
||||
(.-name r))))
|
||||
name1 (if (empty? name) (str (gensym "reagent")) name)]
|
||||
(into {} (for [[k v] (assoc fun-map :displayName name1)]
|
||||
name1 (if (empty? name) (str (gensym "reagent")) name)
|
||||
fmap (if-let [cf (:componentFunction fun-map)]
|
||||
(assoc fun-map :render cf)
|
||||
fun-map)]
|
||||
(into {} (for [[k v] (assoc fmap :displayName name1)]
|
||||
[k (get-wrapper k v name1)]))))
|
||||
|
||||
(defn cljsify [body]
|
||||
|
|
|
@ -7,8 +7,7 @@
|
|||
|
||||
(def React reactimport/React)
|
||||
|
||||
(def cljs-props "cljsProps")
|
||||
(def cljs-children "cljsChildren")
|
||||
(def cljs-argv "cljsArgv")
|
||||
(def cljs-level "cljsLevel")
|
||||
|
||||
(def isClient (not (nil? (try (.-document js/window)
|
||||
|
@ -88,8 +87,12 @@
|
|||
(def input-components #{(aget DOM "input")
|
||||
(aget DOM "textarea")})
|
||||
|
||||
(defn extract-props [v]
|
||||
(let [p (get v 1)]
|
||||
(if (map? p) p)))
|
||||
|
||||
(defn get-props [this]
|
||||
(-> this (aget "props") (aget cljs-props)))
|
||||
(-> this (aget "props") (aget cljs-argv) extract-props))
|
||||
|
||||
(defn input-initial-state [this]
|
||||
(let [props (get-props this)]
|
||||
|
@ -106,7 +109,7 @@
|
|||
(on-change e))))
|
||||
|
||||
(defn input-will-receive-props [this new-props]
|
||||
(let [props (aget new-props cljs-props)]
|
||||
(let [props (-> new-props (aget cljs-argv) extract-props)]
|
||||
(.setState this #js {:value (:value props)
|
||||
:checked (:checked props)})))
|
||||
|
||||
|
@ -119,12 +122,16 @@
|
|||
|
||||
(defn wrapped-render [this comp id-class]
|
||||
(let [inprops (aget this "props")
|
||||
props (aget inprops cljs-props)
|
||||
argv (aget inprops cljs-argv)
|
||||
level (aget inprops cljs-level)
|
||||
props (get argv 1)
|
||||
hasprops (or (nil? props) (map? props))
|
||||
jsargs (->> (aget inprops cljs-children)
|
||||
(map-into-array as-component (inc level)))
|
||||
jsprops (convert-props props id-class)]
|
||||
first-child (if hasprops 2 1)
|
||||
jsargs (if (> (count argv) first-child)
|
||||
(map-into-array as-component (inc level)
|
||||
(subvec argv first-child))
|
||||
(array))
|
||||
jsprops (convert-props (if hasprops props) id-class)]
|
||||
(when (input-components comp)
|
||||
(input-render-setup this jsprops))
|
||||
(.unshift jsargs jsprops)
|
||||
|
@ -132,11 +139,9 @@
|
|||
|
||||
(defn wrapped-should-update [C nextprops nextstate]
|
||||
(let [inprops (aget C "props")
|
||||
p1 (aget inprops cljs-props)
|
||||
c1 (aget inprops cljs-children)
|
||||
p2 (aget nextprops cljs-props)
|
||||
c2 (aget nextprops cljs-children)]
|
||||
(not (util/equal-args p1 c1 p2 c2))))
|
||||
a1 (aget inprops cljs-argv)
|
||||
a2 (aget nextprops cljs-argv)]
|
||||
(not (util/equal-args a1 a2))))
|
||||
|
||||
(defn wrap-component [comp extras name]
|
||||
(let [def #js {:render
|
||||
|
@ -175,7 +180,7 @@
|
|||
|
||||
(defn fn-to-class [f]
|
||||
(let [spec (meta f)
|
||||
withrender (merge spec {:render f})
|
||||
withrender (assoc spec :component-function f)
|
||||
res (reagent.core/create-class withrender)
|
||||
wrapf (.-cljsReactClass res)]
|
||||
(set! (.-cljsReactClass f) wrapf)
|
||||
|
@ -195,15 +200,11 @@
|
|||
|
||||
(defn vec-to-comp [v level]
|
||||
(assert (pos? (count v)))
|
||||
(let [[tag props] v
|
||||
hasmap (map? props)
|
||||
first-child (if (or hasmap (nil? props)) 2 1)
|
||||
c (as-class tag)
|
||||
jsprops (js-obj cljs-props (if hasmap props)
|
||||
cljs-children (if (> (count v) first-child)
|
||||
(subvec v first-child))
|
||||
(let [props (get v 1)
|
||||
c (as-class (v 0))
|
||||
jsprops (js-obj cljs-argv v
|
||||
cljs-level level)]
|
||||
(when hasmap
|
||||
(when (map? props)
|
||||
(let [key (:key props)]
|
||||
(when-not (nil? key)
|
||||
(aset jsprops "key" key))))
|
||||
|
|
|
@ -35,18 +35,6 @@
|
|||
(assert (map? p1))
|
||||
(merge-style p1 (merge-class p1 (merge p1 p2))))))
|
||||
|
||||
(defn identical-parts [v1 v2]
|
||||
;; Compare two vectors using identical?
|
||||
(or (identical? v1 v2)
|
||||
(let [end (count v1)]
|
||||
(and (== end (count v2))
|
||||
(loop [n 0]
|
||||
(if (>= n end)
|
||||
true
|
||||
(if (identical? (nth v1 n) (nth v2 n))
|
||||
(recur (inc n))
|
||||
false)))))))
|
||||
|
||||
(def -not-found (js-obj))
|
||||
|
||||
(defn shallow-equal-maps [x y]
|
||||
|
@ -70,7 +58,16 @@
|
|||
(reduced false))))
|
||||
true x))))
|
||||
|
||||
(defn equal-args [p1 c1 p2 c2]
|
||||
[p1 c1 p2 c2]
|
||||
(and (identical-parts c1 c2)
|
||||
(shallow-equal-maps p1 p2)))
|
||||
(defn equal-args [v1 v2]
|
||||
;; Compare two vectors using identical?
|
||||
(or (identical? v1 v2)
|
||||
(let [end (count v1)]
|
||||
(and (== end (count v2))
|
||||
(loop [n 1]
|
||||
(if (>= n end)
|
||||
true
|
||||
(if (or (identical? (v1 n) (v2 n))
|
||||
(and (== 1 n)
|
||||
(shallow-equal-maps (v1 1) (v2 1))))
|
||||
(recur (inc n))
|
||||
false)))))))
|
||||
|
|
|
@ -52,24 +52,27 @@
|
|||
(let [ran (atom 0)
|
||||
comp (reagent/create-class
|
||||
{:component-did-mount #(swap! ran inc)
|
||||
:render (fn [props children this]
|
||||
:render
|
||||
(fn [this]
|
||||
(let [props (reagent/props this)]
|
||||
(assert (map? props))
|
||||
(assert (= props ((reagent/argv this) 1)))
|
||||
(swap! ran inc)
|
||||
[:div (str "hi " (:foo props) ".")])})]
|
||||
[:div (str "hi " (:foo props) ".")]))})]
|
||||
(with-mounted-component (comp {:foo "you"})
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
(is (found-in #"hi you" div))
|
||||
|
||||
(reagent/set-props C {:foo "there"})
|
||||
(reagent/replace-args C [{:foo "there"}])
|
||||
(is (found-in #"hi there" div))
|
||||
|
||||
(let [runs @ran]
|
||||
(reagent/set-props C {:foo "there"})
|
||||
(reagent/replace-args C [{:foo "there"}])
|
||||
(is (found-in #"hi there" div))
|
||||
(is (= runs @ran)))
|
||||
|
||||
(reagent/replace-props C {:foobar "not used"})
|
||||
(reagent/replace-args C [{:foobar "not used"}])
|
||||
(is (found-in #"hi ." div))))
|
||||
(is (= 5 @ran)))))
|
||||
|
||||
|
@ -78,9 +81,11 @@
|
|||
(let [ran (atom 0)
|
||||
comp (reagent/create-class
|
||||
{:get-initial-state (fn [])
|
||||
:render (fn [props children this]
|
||||
:render
|
||||
(fn []
|
||||
(let [this (reagent/current-component)]
|
||||
(swap! ran inc)
|
||||
[:div (str "hi " (:foo (reagent/state this)))])})]
|
||||
[:div (str "hi " (:foo (reagent/state this)))]))})]
|
||||
(with-mounted-component (comp)
|
||||
(fn [C div]
|
||||
(swap! ran inc)
|
||||
|
@ -164,12 +169,13 @@
|
|||
(deftest init-state-test
|
||||
(when isClient
|
||||
(let [ran (atom 0)
|
||||
really-simple (fn [props children this]
|
||||
really-simple (fn []
|
||||
(let [this (reagent/current-component)]
|
||||
(swap! ran inc)
|
||||
(reagent/set-state this {:foo "foobar"})
|
||||
(fn []
|
||||
[:div (str "this is "
|
||||
(:foo (reagent/state this)))]))]
|
||||
(:foo (reagent/state this)))])))]
|
||||
(with-mounted-component [really-simple nil nil]
|
||||
(fn [c div]
|
||||
(swap! ran inc)
|
||||
|
|
Loading…
Reference in New Issue