Skip to content

Commit

Permalink
defcc macro
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Aug 21, 2015
1 parent 59c694b commit 0f662b7
Show file tree
Hide file tree
Showing 5 changed files with 440 additions and 385 deletions.
26 changes: 19 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,16 +224,16 @@ Each component in Rum has state associated with it. State is just a CLJS map wit

* `:rum/react-component` — link to React component/element object
* `:rum/id` — unique component id
* everything mixins are using for they internal bookkeeping
* anything your own code put here
* everything mixins are using for their internal bookkeeping
* anything you’ve put there (feel free to populate state with your own stuff!)

Reference to current state is stored as `volatile!` boxed value at `props[":rum/state"]`.
Reference to current state is stored as `volatile!` boxed value at `state[":rum/state"]`.
Effectively state is mutable, but components do not change volatile reference directly,
instead all lifecycle functions accept and return state value.

Classes define component behavior, including render function. Class is built from multiple mixins.

Mixins are basic building blocks for designing new components behaviors in Rum. Each mixin is just a map of one or more of following functions and maps:
Mixins are basic building blocks for designing new components behaviors in Rum. Each mixin is just a map of one or more of following functions:

```clojure
{ :init ;; state, props ⇒ state
Expand All @@ -246,9 +246,14 @@ Mixins are basic building blocks for designing new components behaviors in Rum.
:wrap-render ;; render-fn ⇒ render-fn
:did-update ;; state ⇒ state
:will-unmount ;; state ⇒ state
:get-child-context ;; ⇒ child-context
:child-context-types ;; {context-types-for-children}
:context-types ;; {context-types-for-component} }
:child-context ;; state ⇒ child-context }
```

Additionaly, mixin can specify following maps:

```clojure
{ :child-context-types { ... }
:context-types { ... } }
```

Imagine a class built from N mixins. When lifecycle event happens in React (e.g. `componentDidMount`), all `:did-mount` functions from first mixin to last will be invoked one after another, threading current state value through them. State returned from last `:did-mount` mixin will be stored in volatile state reference by Rum. Similarly, `context` maps from multiple mixins are combined into one map.
Expand Down Expand Up @@ -323,6 +328,13 @@ This is a detailed breakdown of what happens inside of Rum. By using `rum/defc`,

## Changes

### 0.3.0

- [ BREAKING ] Component inner state (`:rum/state`) was moved from props to state. It doesn’t change a thing if you were using only Rum API, but might break something if you were relaying on internal details
- Upgraded to React 0.13.3, Sablono 0.3.6, ClojueScript 1.7.48
- Added `defcc` macro for when you only need React component, but not the whole Rum state
- Deprecated `rum/with-props` macro, use `rum/with-key` or `rum/with-ref` fns instead

### 0.2.7

- Allow components to refer to themselves (thx [Kevin Lynagh](https://github.com/lynaghk), pull request #30)
Expand Down
21 changes: 10 additions & 11 deletions examples/examples.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -279,17 +279,16 @@
:value (rum/react ref)
:on-change #(reset! ref (.. % -target -value))}])

(rum/defcs restricting-input < rum/reactive [state ref fn]
(let [comp (:rum/react-component state)]
[:input {:type "text"
:style {:width 170}
:value (rum/react ref)
:on-change #(let [new-val (.. % -target -value)]
(if (fn new-val)
(reset! ref new-val)
;; request-render is mandatory because sablono :input
;; keeps current value in input’s state and always applies changes to it
(rum/request-render comp)))}]))
(rum/defcc restricting-input < rum/reactive [comp ref fn]
[:input {:type "text"
:style {:width 170}
:value (rum/react ref)
:on-change #(let [new-val (.. % -target -value)]
(if (fn new-val)
(reset! ref new-val)
;; request-render is mandatory because sablono :input
;; keeps current value in input’s state and always applies changes to it
(rum/request-render comp)))}])

(rum/defcs restricting-input-native < rum/reactive [state ref fn]
(let [comp (:rum/react-component state)]
Expand Down
13 changes: 12 additions & 1 deletion src/rum.clj
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,19 @@
[& body]
(-defc 'rum/render-state->mixin body))

(defmacro defcc
"Same as defc, but render will take additional first argument: react component
Usage:
(defcc name doc-string? [< mixins+]? [comp params*] render-body+)"
[& body]
(-defc 'rum/render-comp->mixin body))

(defmacro with-props
"Calling function returned by defc will get you component. To specify
"DEPRECATED. Use rum/with-key and rum/with-ref functions
Calling function returned by defc will get you component. To specify
special React properties, create component using with-props:
(rum/with-props <ctor> <arg1> <arg2> :rum/key <key>)
Expand Down
5 changes: 4 additions & 1 deletion src/rum.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[cljsjs.react]
[sablono.core]))

(enable-console-print!)
#_(enable-console-print!)

(let [last-id (volatile! 0)]
(defn next-id []
Expand Down Expand Up @@ -160,6 +160,9 @@
(defn render-state->mixin [render-fn]
{ :render (fn [state] [(apply render-fn state (::args state)) state]) })

(defn render-comp->mixin [render-fn]
{ :render (fn [state] [(apply render-fn (:rum/react-component state) (::args state)) state]) })

(defn args->state [args]
{::args args})

Expand Down
760 changes: 395 additions & 365 deletions target/main.js

Large diffs are not rendered by default.

0 comments on commit 0f662b7

Please sign in to comment.