mirror of https://github.com/status-im/reagent.git
commit
bd8603dce8
|
@ -4,9 +4,13 @@
|
|||
|
||||
**[compare](https://github.com/reagent-project/reagent/compare/v0.8.1...master)**
|
||||
|
||||
- Default to React 16.9
|
||||
- Fix using `with-let` macro in namespaces with `*warn-on-infer*` enabled ([#420](https://github.com/reagent-project/reagent/issues/420))
|
||||
- Fix using metadata to set React key with Fragment shortcut (`:<>`) ([#401](https://github.com/reagent-project/reagent/issues/401))
|
||||
- Create React Component without `create-react-class` ([#416](https://github.com/reagent-project/reagent/issues/416))
|
||||
- `React.Component` doesn't have `getInitialState` method, but this is implemented by
|
||||
Reagent for compatibility with old components.
|
||||
- `constructor` can be used to initialize components (e.g. set the state)
|
||||
- Allow any number of arguments for `reagent.core/merge-props` and
|
||||
ensure `:class` is merged correctly when it is defined as collection. ([#412](https://github.com/reagent-project/reagent/issues/412))
|
||||
- Add `reagent.core/class-names` utility functions which can be used
|
||||
|
@ -17,6 +21,9 @@ uses correct Object interop forms, allowing use of ClojureScript `:checked-array
|
|||
- Deprecated `reagent.interop` namespace
|
||||
- It is better to use proper object interop forms or `goog.object` functions instead.
|
||||
- Drop `:export` metadata from `force-update-all` function
|
||||
- `componentWillReceiveProps`, `componentWillUpdate` and `componentWillMount` lifecycle methods are deprecated
|
||||
- Using these directly will show warning, using `UNSAFE_` prefixed version will silence the warning.
|
||||
- These methods will continue to work with React 16.9 and 17.
|
||||
|
||||
## 0.8.1 (2018-05-15)
|
||||
|
||||
|
|
|
@ -3,8 +3,11 @@
|
|||
"dependencies": {
|
||||
"@cljs-oss/module-deps": "1.1.1",
|
||||
"prop-types": "15.6.2",
|
||||
"react": "16.8.6",
|
||||
"react-dom": "16.8.6"
|
||||
"react": "16.9.0",
|
||||
"react-dom": "16.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "lein figwheel client-npm"
|
||||
},
|
||||
"devDependencies": {
|
||||
"gzip-size-cli": "3.0.0",
|
||||
|
|
|
@ -7,9 +7,9 @@
|
|||
;; If :npm-deps enabled, these are used only for externs.
|
||||
;; Without direct react dependency, other packages,
|
||||
;; like react-leaflet might have closer dependency to a other version.
|
||||
[cljsjs/react "16.8.6-0"]
|
||||
[cljsjs/react-dom "16.8.6-0"]
|
||||
[cljsjs/react-dom-server "16.8.6-0"]]
|
||||
[cljsjs/react "16.9.0-0"]
|
||||
[cljsjs/react-dom "16.9.0-0"]
|
||||
[cljsjs/react-dom-server "16.9.0-0"]]
|
||||
|
||||
:plugins [[lein-cljsbuild "1.1.7"]
|
||||
[lein-doo "0.1.11"]
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
{:npm-deps {"react" "16.8.6"
|
||||
"react-dom" "16.8.6"}}
|
||||
{:npm-deps {"react" "16.9.0"
|
||||
"react-dom" "16.9.0"}}
|
||||
|
|
|
@ -106,22 +106,40 @@
|
|||
"Creates JS class based on provided Clojure map, for example:
|
||||
|
||||
```cljs
|
||||
{:get-initial-state (fn [this])
|
||||
:component-will-receive-props (fn [this new-argv])
|
||||
{;; Constructor
|
||||
:constructor (fn [this props])
|
||||
:get-initial-state (fn [this])
|
||||
;; Static methods
|
||||
:get-derived-state-from-props (fn [props state] partial-state)
|
||||
:get-derived-state-from-error (fn [error] partial-state)
|
||||
;; Methods
|
||||
:get-snapshot-before-update (fn [this old-argv new-argv] snapshot)
|
||||
: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-argv])
|
||||
:component-did-update (fn [this old-argv])
|
||||
:component-did-update (fn [this old-argv old-state snapshot])
|
||||
:component-will-unmount (fn [this])
|
||||
:reagent-render (fn [args....])} ;; or :render (fn [this])
|
||||
:component-did-catch (fn [this error info])
|
||||
:reagent-render (fn [args....])
|
||||
;; Or alternatively:
|
||||
:render (fn [this])
|
||||
;; Deprecated methods:
|
||||
:UNSAFE_component-will-receive-props (fn [this new-argv])
|
||||
:UNSAFE_component-will-update (fn [this new-argv new-state])
|
||||
:UNSAFE_component-will-mount (fn [this])}
|
||||
```
|
||||
|
||||
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`.
|
||||
|
||||
State can be initialized using constructor, which matches React.Component class,
|
||||
or using getInitialState which matches old React createClass function and is
|
||||
now implemented by Reagent for compatibility.
|
||||
|
||||
State can usually be anything, e.g. Cljs object. But if using getDerivedState
|
||||
methods, the state has to be plain JS object as React implementation uses
|
||||
Object.assign to merge partial state into the current state.
|
||||
|
||||
React built-in static methods or properties are automatically defined as statics."
|
||||
[spec]
|
||||
|
|
|
@ -23,9 +23,14 @@
|
|||
(.-msRequestAnimationFrame w)
|
||||
fake-raf))))
|
||||
|
||||
(defn compare-mount-order [c1 c2]
|
||||
(- (.-cljsMountOrder c1)
|
||||
(.-cljsMountOrder c2)))
|
||||
(defn compare-mount-order
|
||||
[c1 c2]
|
||||
;; Mount order is now set in DidMount method. I.e. the
|
||||
;; top-most component is mounted last and gets largest
|
||||
;; number. This is reverse compared to WillMount where method
|
||||
;; for top component gets called first.
|
||||
(- (.-cljsMountOrder c2)
|
||||
(.-cljsMountOrder c1)))
|
||||
|
||||
(defn run-queue [a]
|
||||
;; sort components by mount order, to make sure parents
|
||||
|
|
|
@ -162,15 +162,30 @@
|
|||
:getDefaultProps
|
||||
(throw (js/Error. "getDefaultProps not supported"))
|
||||
|
||||
:getDerivedStateFromProps
|
||||
(fn getDerivedStateFromProps [props state]
|
||||
;; Read props from Reagent argv
|
||||
(.call f nil (if-some [a (.-argv props)] (extract-props a) props) state))
|
||||
|
||||
;; In ES6 React, this is now part of the constructor
|
||||
:getInitialState
|
||||
(fn getInitialState [c]
|
||||
(reset! (state-atom c) (.call f c c)))
|
||||
|
||||
:getSnapshotBeforeUpdate
|
||||
(fn getSnapshotBeforeUpdate [oldprops oldstate]
|
||||
(this-as c (.call f c c (props-argv c oldprops) oldstate)))
|
||||
|
||||
;; Deprecated - warning in 16.9 will work through 17.x
|
||||
:componentWillReceiveProps
|
||||
(fn componentWillReceiveProps [nextprops]
|
||||
(this-as c (.call f c c (props-argv c nextprops))))
|
||||
|
||||
;; Deprecated - will work in 17.x
|
||||
:UNSAFE_componentWillReceiveProps
|
||||
(fn componentWillReceiveProps [nextprops]
|
||||
(this-as c (.call f c c (props-argv c nextprops))))
|
||||
|
||||
:shouldComponentUpdate
|
||||
(fn shouldComponentUpdate [nextprops nextstate]
|
||||
(or util/*always-update*
|
||||
|
@ -188,24 +203,38 @@
|
|||
noargv (.call f c c (get-argv c) (props-argv c nextprops))
|
||||
:else (.call f c c old-argv new-argv))))))
|
||||
|
||||
;; Deprecated - warning in 16.9 will work through 17.x
|
||||
:componentWillUpdate
|
||||
(fn componentWillUpdate [nextprops]
|
||||
(this-as c (.call f c c (props-argv c nextprops))))
|
||||
(fn componentWillUpdate [nextprops nextstate]
|
||||
(this-as c (.call f c c (props-argv c nextprops) nextstate)))
|
||||
|
||||
;; Deprecated - will work in 17.x
|
||||
:UNSAFE_componentWillUpdate
|
||||
(fn componentWillUpdate [nextprops nextstate]
|
||||
(this-as c (.call f c c (props-argv c nextprops) nextstate)))
|
||||
|
||||
:componentDidUpdate
|
||||
(fn componentDidUpdate [oldprops]
|
||||
(this-as c (.call f c c (props-argv c oldprops))))
|
||||
(fn componentDidUpdate [oldprops oldstate snapshot]
|
||||
(this-as c (.call f c c (props-argv c oldprops) oldstate snapshot)))
|
||||
|
||||
;; Deprecated - warning in 16.9 will work through 17.x
|
||||
:componentWillMount
|
||||
(fn componentWillMount []
|
||||
(this-as c
|
||||
(set! (.-cljsMountOrder c) (batch/next-mount-count))
|
||||
(when-not (nil? f)
|
||||
(.call f c c))))
|
||||
(this-as c (.call f c c)))
|
||||
|
||||
;; Deprecated - will work in 17.x
|
||||
:UNSAFE_componentWillMount
|
||||
(fn componentWillMount []
|
||||
(this-as c (.call f c c)))
|
||||
|
||||
:componentDidMount
|
||||
(fn componentDidMount []
|
||||
(this-as c (.call f c c)))
|
||||
(this-as c
|
||||
;; This method is called after everything inside the
|
||||
;; has been mounted. This is reverse compared to WillMount.
|
||||
(set! (.-cljsMountOrder c) (batch/next-mount-count))
|
||||
(when-not (nil? f)
|
||||
(.call f c c))))
|
||||
|
||||
:componentWillUnmount
|
||||
(fn componentWillUnmount []
|
||||
|
@ -230,14 +259,14 @@
|
|||
;; 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
|
||||
:componentDidMount nil
|
||||
:componentWillUnmount nil})
|
||||
|
||||
(def dash-to-camel (util/memoize-1 util/dash-to-camel))
|
||||
(def dash-to-method-name (util/memoize-1 util/dash-to-method-name))
|
||||
|
||||
(defn camelify-map-keys [fun-map]
|
||||
(reduce-kv (fn [m k v]
|
||||
(assoc m (-> k dash-to-camel keyword) v))
|
||||
(assoc m (-> k dash-to-method-name keyword) v))
|
||||
{} fun-map))
|
||||
|
||||
(defn add-obligatory [fun-map]
|
||||
|
@ -297,17 +326,20 @@
|
|||
[body]
|
||||
{:pre [(map? body)]}
|
||||
(let [body (cljsify body)
|
||||
methods (map-to-js (apply dissoc body :displayName :getInitialState
|
||||
methods (map-to-js (apply dissoc body :displayName :getInitialState :constructor
|
||||
: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)
|
||||
get-initial-state (:getInitialState body)
|
||||
construct (:constructor body)
|
||||
cmp (fn [props context updater]
|
||||
(this-as this
|
||||
(.call react/Component this props context updater)
|
||||
(when construct
|
||||
(construct this))
|
||||
(construct this props))
|
||||
(when get-initial-state
|
||||
(set! (.-state this) (get-initial-state this)))
|
||||
this))]
|
||||
|
||||
(gobj/extend (.-prototype cmp) (.-prototype react/Component) methods)
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
(if (named? k)
|
||||
(if-some [k' (cache-get prop-name-cache (name k))]
|
||||
k'
|
||||
(let [v (util/dash-to-camel k)]
|
||||
(let [v (util/dash-to-prop-name k)]
|
||||
(gobj/set prop-name-cache (name k))
|
||||
v))
|
||||
k))
|
||||
|
@ -78,7 +78,7 @@
|
|||
(if (named? k)
|
||||
(if-some [k' (cache-get custom-prop-name-cache (name k))]
|
||||
k'
|
||||
(let [v (util/dash-to-camel k)]
|
||||
(let [v (util/dash-to-prop-name k)]
|
||||
(gobj/set custom-prop-name-cache (name k) v)
|
||||
v))
|
||||
k))
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
(string/upper-case s)
|
||||
(str (string/upper-case (subs s 0 1)) (subs s 1))))
|
||||
|
||||
(defn dash-to-camel [dashed]
|
||||
(defn dash-to-prop-name [dashed]
|
||||
(if (string? dashed)
|
||||
dashed
|
||||
(let [name-str (name dashed)
|
||||
|
@ -37,6 +37,14 @@
|
|||
name-str
|
||||
(apply str start (map capitalize parts))))))
|
||||
|
||||
(defn dash-to-method-name [dashed]
|
||||
(if (string? dashed)
|
||||
dashed
|
||||
(let [name-str (name dashed)
|
||||
name-str (string/replace name-str #"(unsafe|UNSAFE)[-_]" "UNSAFE_")
|
||||
[start & parts] (string/split name-str #"-")]
|
||||
(apply str start (map capitalize parts)))))
|
||||
|
||||
(defn fun-name [f]
|
||||
(let [n (or (and (fn? f)
|
||||
(or (.-displayName f)
|
||||
|
|
|
@ -19,6 +19,20 @@
|
|||
(is (= "a b c d"
|
||||
(util/class-names "a" "b" nil ["c" "d"]))))
|
||||
|
||||
(deftest dash-to-prop-name-test
|
||||
(is (= "tabIndex" (util/dash-to-prop-name :tab-index)))
|
||||
(is (= "data-foo-bar" (util/dash-to-prop-name :data-foo-bar))))
|
||||
|
||||
(deftest dash-to-method-name-test
|
||||
(is (= "componentDidMount"
|
||||
(util/dash-to-method-name :component-did-mount)))
|
||||
(is (= "componentDidMount"
|
||||
(util/dash-to-method-name :componentDidMount)))
|
||||
(is (= "UNSAFE_componentDidMount"
|
||||
(util/dash-to-method-name :unsafe-component-did-mount)))
|
||||
(is (= "UNSAFE_componentDidMount"
|
||||
(util/dash-to-method-name :unsafe_componentDidMount))))
|
||||
|
||||
; (simple-benchmark []
|
||||
; (do (util/class-names "a" "b")
|
||||
; (util/class-names nil "a")
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
[reagent.dom.server :as server]
|
||||
[reagent.impl.util :as util]
|
||||
[reagenttest.utils :as u :refer [with-mounted-component found-in]]
|
||||
[clojure.string :as string]
|
||||
[goog.string :as gstr]
|
||||
[goog.object :as gobj]
|
||||
[prop-types :as prop-types]))
|
||||
|
@ -740,7 +741,7 @@
|
|||
(reset! t (first args))
|
||||
(add-args :initial-state args)
|
||||
{:foo "bar"})
|
||||
:component-will-mount
|
||||
:UNSAFE_component-will-mount
|
||||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :will-mount args))
|
||||
|
@ -752,11 +753,11 @@
|
|||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :should-update args) true)
|
||||
:component-will-receive-props
|
||||
:UNSAFE_component-will-receive-props
|
||||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :will-receive args))
|
||||
:component-will-update
|
||||
:UNSAFE_component-will-update
|
||||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :will-update args))
|
||||
|
@ -794,11 +795,11 @@
|
|||
(is (= (:should-update @res)
|
||||
{:at 6 :args [@t [@comp "a" "b"] [@comp "a" "c"]]}))
|
||||
(is (= (:will-update @res)
|
||||
{:at 7 :args [@t [@comp "a" "c"]]}))
|
||||
{:at 7 :args [@t [@comp "a" "c"] {:foo "bar"}]}))
|
||||
(is (= (:render @res)
|
||||
{:at 8 :args ["a" "c"]}))
|
||||
(is (= (:did-update @res)
|
||||
{:at 9 :args [@t [@comp "a" "b"]]})))]
|
||||
{:at 9 :args [@t [@comp "a" "b"] {:foo "bar"} nil]})))]
|
||||
(when isClient
|
||||
(with-mounted-component [c2] check)
|
||||
(is (= (:will-unmount @res)
|
||||
|
@ -839,7 +840,7 @@
|
|||
(reset! oldprops (-> args first r/props))
|
||||
(add-args :initial-state args)
|
||||
{:foo "bar"})
|
||||
:component-will-mount
|
||||
:UNSAFE_component-will-mount
|
||||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :will-mount args))
|
||||
|
@ -851,14 +852,14 @@
|
|||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :should-update args) true)
|
||||
:component-will-receive-props
|
||||
:UNSAFE_component-will-receive-props
|
||||
(fn [& args]
|
||||
(reset! newprops (-> args second second))
|
||||
(this-as c
|
||||
(is (= c (first args)))
|
||||
(add-args :will-receive (into [(dissoc (r/props c) :children)]
|
||||
(:children (r/props c))))))
|
||||
:component-will-update
|
||||
:UNSAFE_component-will-update
|
||||
(fn [& args]
|
||||
(this-as c (is (= c (first args))))
|
||||
(add-args :will-update args))
|
||||
|
@ -1266,3 +1267,82 @@
|
|||
(is (not @error-thrown-after-updating-props)))))]
|
||||
(is (re-find #"Warning: Exception thrown while comparing argv's in shouldComponentUpdate:"
|
||||
(first (:warn e))))))))
|
||||
|
||||
(deftest get-derived-state-from-props-test
|
||||
(when isClient
|
||||
(let [prop (r/atom 0)
|
||||
;; Usually one can use Cljs object as React state. However,
|
||||
;; getDerivedStateFromProps implementation in React uses
|
||||
;; Object.assign to merge current state and partial state returned
|
||||
;; from the method, so the state has to be plain old object.
|
||||
pure-component (r/create-class
|
||||
{:constructor (fn [this]
|
||||
(set! (.-state this) #js {}))
|
||||
:get-derived-state-from-props (fn [props state]
|
||||
;; "Expensive" calculation based on the props
|
||||
#js {:v (string/join " " (repeat (inc (:value props)) "foo"))})
|
||||
:render (fn [this]
|
||||
(r/as-element [:p "Value " (gobj/get (.-state this) "v")]))})
|
||||
component (fn []
|
||||
[pure-component {:value @prop}])]
|
||||
(with-mounted-component [component]
|
||||
(fn [c div]
|
||||
(is (found-in #"Value foo" div))
|
||||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (found-in #"Value foo foo" div)))))))
|
||||
|
||||
(deftest get-derived-state-from-error-test
|
||||
(when isClient
|
||||
(let [prop (r/atom 0)
|
||||
component (r/create-class
|
||||
{:constructor (fn [this props]
|
||||
(set! (.-state this) #js {:hasError false}))
|
||||
:get-derived-state-from-error (fn [error]
|
||||
#js {:hasError true})
|
||||
:component-did-catch (fn [this e info])
|
||||
:render (fn [this]
|
||||
(js/console.log (r/children this))
|
||||
(r/as-element (if (.-hasError (.-state this))
|
||||
[:p "Error"]
|
||||
(into [:<>] (r/children this)))))})
|
||||
bad-component (fn []
|
||||
(if (= 0 @prop)
|
||||
[:div "Ok"]
|
||||
(throw (js/Error. "foo"))))]
|
||||
(wrap-capture-window-error
|
||||
(wrap-capture-console-error
|
||||
#(with-mounted-component [component [bad-component]]
|
||||
(fn [c div]
|
||||
(is (found-in #"Ok" div))
|
||||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (found-in #"Error" div)))))))))
|
||||
|
||||
(deftest get-snapshot-before-update-test
|
||||
(when isClient
|
||||
(let [ref (react/createRef)
|
||||
prop (r/atom 0)
|
||||
did-update (atom nil)
|
||||
component (r/create-class
|
||||
{:get-snapshot-before-update (fn [this [_ prev-props] prev-state]
|
||||
{:height (.. ref -current -scrollHeight)})
|
||||
:component-did-update (fn [this [_ prev-props] prev-state snapshot]
|
||||
(reset! did-update snapshot))
|
||||
:render (fn [this]
|
||||
(r/as-element
|
||||
[:div
|
||||
{:ref ref
|
||||
:style {:height "20px"}}
|
||||
"foo"]))})
|
||||
component-2 (fn []
|
||||
[component {:value @prop}])]
|
||||
(with-mounted-component [component-2]
|
||||
(fn [c div]
|
||||
;; Attach to DOM to get real height value
|
||||
(.appendChild js/document.body div)
|
||||
(is (found-in #"foo" div))
|
||||
(swap! prop inc)
|
||||
(r/flush)
|
||||
(is (= {:height 20} @did-update))
|
||||
(.removeChild js/document.body div))))))
|
||||
|
|
Loading…
Reference in New Issue