Skip to content

Commit

Permalink
0.11.3 updated docstrings
Browse files Browse the repository at this point in the history
  • Loading branch information
tonsky committed Nov 28, 2018
1 parent 1dcbabb commit d615089
Show file tree
Hide file tree
Showing 13 changed files with 586 additions and 183 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.11.3

- Docstrings for https://cljdoc.org/d/rum/rum

## 0.11.2

- Server-render on-* event handlers with string values
Expand Down
6 changes: 6 additions & 0 deletions doc/cljdoc.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:cljdoc.doc/tree [
["Readme" {:file "README.md"}]
["Changelog" {:file "CHANGELOG.md"}]
["Useful mixins" {:file "doc/useful-mixins.md"}]
["React interop" {:file "doc/react-interop.md"}]
]}
109 changes: 109 additions & 0 deletions doc/react-interop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
# Use different React version

Add to `project.clj`:

```
:dependencies {
[rum "0.11.3" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
[cljsjs/react "16.6.0-0"]
[cljsjs/react-dom "16.6.0-0"]
}
```

# Including React.js manually

If you want to include `react.js` yourself, then add this to `project.clj`:

```
:dependencies {
[rum "0.11.3" :exclusions [[cljsjs/react] [cljsjs/react-dom]]]
}
```

Create two files

1. `src/cljsjs/react.cljs`:

```
(ns cljsjs.react)
```

2. `src/cljsjs/react/dom.cljs`:

```
(ns cljsjs.react.dom)
```

Add to your HTML the version you want:

```
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
```

# Using React with addons

```clj
[rum "0.11.3" :exclusions [cljsjs/react cljsjs/react-dom]]
[cljsjs/react-dom "16.2.0-3" :exclusions [cljsjs/react]]
[cljsjs/react-dom-server "16.2.0-3" :exclusions [cljsjs/react]]
[cljsjs/react-with-addons "16.2.0-3"]
```

# Profiling with [React perf](https://facebook.github.io/react/docs/perf.html)

Specify the `react-with-addons` dependency in your `project.clj` (see above ↑)

Then from within your program run:

```clj
(js/React.addons.Perf.start)
;;run your app
(js/React.addons.Perf.stop)
(js/React.addons.Perf.printWasted)
```

and results will be printed to the developer console.

# Using 3rd-party React components

Given e.g. [react-router-transition](https://github.com/maisano/react-router-transition)

```clj
(defn route-transition [pathname children]
(js/React.createElement js/RouteTransition
#js { :pathname pathname
:atEnter #js { :opacity 0 }
:atLeave #js { opacity: 0 }
:atActive #js { opacity: 1 } }
(clj->js children)))
```

Another example [react-component/slider](https://github.com/react-component/slider)

```clj
(defn range-slider [min max]
(js/React.createElement js/Slider #js { :min min
:max max
:range true
:defaultValue #js [40 60] }))
```

**Note:** See how `defn` is used here instead of `defc`? Using `defc` would cause two components being created (e.g. `range-slider` and the `Slider` component). Because in many cases you don't need the wrapping component you can just use `defn`.

# Get displayName of component

This might be useful for development when you want to know which component this mixin is handling:

```clojure
(ns ...
(:require [goog.object :as gobj]))

(defn display-name
"Returns the displayname of the component"
[state]
(gobj/getValueByKeys
(:rum/react-component state)
"constructor"
"displayName"))
```
184 changes: 184 additions & 0 deletions doc/useful-mixins.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# Request stuff on mount by AJAX

Components using this mixin will do an AJAX request on mount and will update themselves when they got a reply. Mixin puts an atom to the state whose value (after deref) is either nil (request pending) or returned value.

```clojure
(defn ajax-mixin [url key]
{ :will-mount
(fn [state]
(let [*data (atom nil)
comp (:rum/react-component state)]
(ajax
url
(fn [data]
(reset! *data data)
(rum/request-render comp)))
(assoc state key *data))) })


(rum/defcs user-info < (ajax-mixin "/api/user/info" ::user)
[state]
(if-let [user @(::user state)]
...
[:div "Loading..."]))
```

Customize to your taste: dynamic URL generation from component args, AJAX retries, failed state, callback on finish, deserialization.

# Debouncer

```clojure
(ns ... (:import [goog.async Debouncer]))

(defn debouncer-mixin
"Creates a debouncer in (:debouncer state) which can be called with (.fire).
Invokes the callback cb or invokes the first argument passed to fire.
Usage:
1. (debouncer-mixin 200)
(.fire (:debouncer state) #(do-actual-action ...))
2. (debouncer-mixin 200 #(do-an-action ...))
(.fire (:debouncer state))"
([ms] (debouncer-mixin ms nil))
([ms cb]
{:will-mount
(fn debouncer-mount [state]
(assoc state :debouncer (Debouncer. (if (nil? cb) #(%1) cb) ms)))
:will-unmount
(fn debouncer-unmount [state]
(.dispose (:debouncer state))
state)}))
```

# Install CSS styles on mount

For your root rum component, you can install CSS styles on mount and uninstall them on unmount.
This is useful if you have your (garden) styles defined in `cljc` file. In production you generate
a css file and include it with a normal `<style>` tag, in development however, you can just use this
mixin to have them automatically be applied as soon as you change any style.

```clojure
(ns ...
(:require [goog.style :as gstyle]))

(defn install-styles-mixin
"Installes the stylesheet when mounting. Uninstall when unmount.
Useful for use at the very root render and use with garden in a cljc environment.
Live update of CSS without needing to go trough figwheel :)"
[css-str]
{:will-mount
(fn [st]
(assoc st ::stylesheet (gstyle/installStyles css-str)))
:will-unmount
(fn [st]
(gstyle/uninstallStyles (::stylesheet st))
st)})

;; Then use them like so:
app < (install-styles-mixin (your-cljc/gen-css))
```

# Measure render

You can measure how long a component needs to render with this mixin:

```clojure
(defn perf-measure-mixin
[desc]
"Does performance measurements in development."
{:wrap-render
(fn wrap-render [render-fn]
(fn [state]
(profile
(str "Render " desc)
(render-fn state))))})
;; where profile is a macro like this:
(defmacro profile [k & body]
(if macros/dev?
`(let [k# ~k]
(.time js/console k#)
(let [res# (do ~@body)]
(.timeEnd js/console k#)
res#))
`(do ~@body)))
```

# Keyboard shortcut

Install a keyboard shortcut that is only valid while the component is mounted:

```clojure
(defn keyboard-mixin
"Triggers f when key is pressed while the component is mounted.
if target is a function it will be called AFTER the component mounted
with state and should return a dom node that is the target of the listener.
If no target is given it is defaulted to js/window (global handler)
Ex:
(keyboard-mixin \"esc\" #(browse-to :home/home))"
([key f] (keyboard-mixin key f js/window))
([key f target]
(let [target-fn (if (fn? target) target (fn [_] target))]
{:did-mount
(fn [state]
(assoc state ::keyboard-listener
(keyboard/install-shortcut! key f false (target-fn state))))
:will-unmount
(fn [state]
((::keyboard-listener state))
state)})))

;; where install-shortcut! is:
(ns you-tools.keyboard
(:require [goog.events :as events]
[goog.ui.KeyboardShortcutHandler.EventType :as EventType]
[goog.events.KeyCodes :as KeyCodes])
(:import [goog.ui KeyboardShortcutHandler]))

(defn install-shortcut!
"Installs a Keyboard Shortcut handler.
The key is a string the trigger is a function that will receive the keyboard event as the
first argument. If once? is true the keyboard shortcut is only fired once.
The unregister handler is returned and can be called to unregister the listener.
If target is not given it's attached to window."
([key trigger] (install-shortcut! key trigger false js/window))
([key trigger once?] (install-shortcut! key trigger once? js/window))
([key trigger once? target]
(let [handler (new KeyboardShortcutHandler target)]
(.registerShortcut handler (str key once?) key)
(events/listen
handler
EventType/SHORTCUT_TRIGGERED
(fn [e]
(trigger e)
(when once?
(.unregisterShortcut handler keys))))
(fn []
(.unregisterShortcut handler key)))))
```

# Remember state

This mixin can be used to remember the state of a component. For this to work you need to UNIQUELY identify
your component somehow. For instance, by generating some unique id from the given args to your component:

```clojure
(defn remember-state-mixin
"Remembers the state :rum/local for a given component and swaps it back on mount.
The given function is passed the args of the component (with apply).
And should return a map key that is used to uniquely identify the component.
(remember-state-mixin (fn [arg1 arg2] (:db/id arg1)))"
[f]
(let [store (atom {})
key-fn (fn [state] (apply f (:rum/args state)))]
{:will-unmount
(fn remember-state-unmount [state]
(swap! store assoc (key-fn state) @(:rum/local state))
state)
:will-mount
(fn remember-state-mount [state]
(let [old-state @store
key (key-fn state)]
(swap! (:rum/local state) merge (get old-state key {})))
state)}))
```
2 changes: 1 addition & 1 deletion project.clj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
(defproject rum "0.11.2"
(defproject rum "0.11.3"
:description "ClojureScript wrapper for React"
:license { :name "Eclipse"
:url "http://www.eclipse.org/legal/epl-v10.html" }
Expand Down
Loading

0 comments on commit d615089

Please sign in to comment.