-
-
Notifications
You must be signed in to change notification settings - Fork 68
/
core.cljs
374 lines (305 loc) · 12 KB
/
core.cljs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
(ns ^{:mranderson/inlined true} day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.core
(:require-macros [day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.core])
(:refer-clojure :exclude [partial atom flush])
(:require [react :as react]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.impl.template :as tmpl]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.impl.component :as comp]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.impl.util :as util]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.impl.batching :as batch]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.impl.protocols :as p]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.ratom :as ratom]
[day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.debug :as deb :refer-macros [assert-some assert-component
assert-js-object assert-new-state
assert-callable]]))
(def is-client util/is-client)
(defn create-element
"Create a native React element, by calling React.createElement directly.
That means the second argument must be a javascript object (or nil), and
that any Reagent hiccup forms must be processed with as-element. For example
like this:
```cljs
(r/create-element \"div\" #js{:className \"foo\"}
\"Hi \" (r/as-element [:strong \"world!\"])
```
which is equivalent to
```cljs
[:div.foo \"Hi\" [:strong \"world!\"]]
```"
([type]
(create-element type nil))
([type props]
(assert-js-object props)
(react/createElement type props))
([type props child]
(assert-js-object props)
(react/createElement type props child))
([type props child & children]
(assert-js-object props)
(apply react/createElement type props child children)))
(defn as-element
"Turns a vector of Hiccup syntax into a React element. Returns form
unchanged if it is not a vector."
([form] (p/as-element tmpl/default-compiler form))
([form compiler] (p/as-element compiler form)))
(defn adapt-react-class
"Returns an adapter for a native React class, that may be used
just like a Reagent component function or class in Hiccup forms."
[c]
(assert-some c "Component")
(tmpl/adapt-react-class c))
(defn reactify-component
"Returns an adapter for a Reagent component, that may be used from
React, for example in JSX. A single argument, props, is passed to
the component, converted to a map."
([c] (reactify-component c tmpl/default-compiler))
([c compiler]
(assert-some c "Component")
(comp/reactify-component c compiler)))
(defn create-class
"Creates JS class based on provided Clojure map, for example:
```cljs
{;; 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-did-mount (fn [this])
:component-did-update (fn [this old-argv old-state snapshot])
:component-will-unmount (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.
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]
(comp/create-class spec tmpl/default-compiler))
([spec compiler]
(comp/create-class spec compiler)))
(defn current-component
"Returns the current React component (a.k.a `this`) in a component
function."
[]
comp/*current-component*)
(defn state-atom
"Returns an atom containing a components state."
[this]
(assert-component this)
(comp/state-atom this))
(defn state
"Returns the state of a component, as set with replace-state or set-state.
Equivalent to `(deref (r/state-atom this))`"
[this]
(assert-component this)
(deref (state-atom this)))
(defn replace-state
"Set state of a component.
Equivalent to `(reset! (state-atom this) new-state)`"
[this new-state]
(assert-component this)
(assert-new-state new-state)
(reset! (state-atom this) new-state))
(defn set-state
"Merge component state with new-state.
Equivalent to `(swap! (state-atom this) merge new-state)`"
[this new-state]
(assert-component this)
(assert-new-state new-state)
(swap! (state-atom this) merge new-state))
(defn force-update
"Force a component to re-render immediately.
If the second argument is true, child components will also be
re-rendered, even is their arguments have not changed."
([this]
(force-update this false))
([this deep]
(ratom/flush!)
(util/force-update this deep)
(batch/flush-after-render)))
(defn props
"Returns the props passed to a component."
[this]
(assert-component this)
(comp/get-props this))
(defn children
"Returns the children passed to a component."
[this]
(assert-component this)
(comp/get-children this))
(defn argv
"Returns the entire Hiccup form passed to the component."
[this]
(assert-component this)
(comp/get-argv this))
(defn class-names
"Function which normalizes and combines class values to a string
Reagent allows classes to be defined as:
- Strings
- Named objects (Symbols or Keywords)
- Collections of previous types"
([])
([class] (util/class-names class))
([class1 class2] (util/class-names class1 class2))
([class1 class2 & others] (apply util/class-names class1 class2 others)))
(defn merge-props
"Utility function that merges some maps, handling `:class` and `:style`.
The :class value is always normalized (using `class-names`) even if no
merging is done."
([] (util/merge-props))
([defaults] (util/merge-props defaults))
([defaults props] (util/merge-props defaults props))
([defaults props & others] (apply util/merge-props defaults props others)))
(defn flush
"Render dirty components immediately.
Note that this may not work in event handlers, since React.js does
batching of updates there."
[]
(batch/flush))
;; Ratom
(defn atom
"Like clojure.core/atom, except that it keeps track of derefs.
Reagent components that derefs one of these are automatically
re-rendered."
([x] (ratom/atom x))
([x & rest] (apply ratom/atom x rest)))
(defn track
"Takes a function and optional arguments, and returns a derefable
containing the output of that function. If the function derefs
Reagent atoms (or track, etc), the value will be updated whenever
the atom changes.
In other words, `@(track foo bar)` will produce the same result
as `(foo bar)`, but foo will only be called again when the atoms it
depends on changes, and will only trigger updates of components when
its result changes.
track is lazy, i.e the function is only evaluated on deref."
[f & args]
{:pre [(ifn? f)]}
(ratom/make-track f args))
(defn track!
"An eager version of track. The function passed is called
immediately, and continues to be called when needed, until stopped
with dispose!."
[f & args]
{:pre [(ifn? f)]}
(ratom/make-track! f args))
(defn dispose!
"Stop the result of track! from updating."
[x]
(ratom/dispose! x))
(defn wrap
"Provide a combination of value and callback, that looks like an atom.
The first argument can be any value, that will be returned when the
result is deref'ed.
The second argument should be a function, that is called with the
optional extra arguments provided to wrap, and the new value of the
resulting 'atom'.
Use for example like this:
```cljs
(wrap (:foo @state)
swap! state assoc :foo)
```
Probably useful only for passing to child components."
[value reset-fn & args]
(assert-callable reset-fn)
(ratom/make-wrapper value reset-fn args))
;; RCursor
(defn cursor
"Provide a cursor into a Reagent atom.
Behaves like a Reagent atom but focuses updates and derefs to
the specified path within the wrapped Reagent atom. e.g.,
```cljs
(let [c (cursor ra [:nested :content])]
... @c ;; equivalent to (get-in @ra [:nested :content])
... (reset! c 42) ;; equivalent to (swap! ra assoc-in [:nested :content] 42)
... (swap! c inc) ;; equivalence to (swap! ra update-in [:nested :content] inc)
)
```
The first parameter can also be a function, that should look
something like this:
```cljs
(defn set-get
([k] (get-in @state k))
([k v] (swap! state assoc-in k v)))
```
The function will be called with one argument – the path passed to
cursor – when the cursor is deref'ed, and two arguments (path and
new value) when the cursor is modified.
Given that set-get function, (and that state is a Reagent atom, or
another cursor) these cursors are equivalent:
`(cursor state [:foo])` and `(cursor set-get [:foo])`.
Note that a cursor is lazy: its value will not change until it is
used. This may be noticed with add-watch."
([src path]
(ratom/cursor src path)))
;; Utilities
(defn rswap!
"Swaps the value of a to be `(apply f current-value-of-atom args)`.
rswap! works like swap!, except that recursive calls to rswap! on
the same atom are allowed – and it always returns nil."
[^IAtom a f & args]
{:pre [(satisfies? IAtom a)
(ifn? f)]}
(if (.-rswapping a)
(-> (or (.-rswapfs a)
(set! (.-rswapfs a) (array)))
(.push #(apply f % args)))
(do (set! (.-rswapping a) true)
(try (swap! a (fn [state]
(loop [s (apply f state args)]
(if-some [sf (some-> a .-rswapfs .shift)]
(recur (sf s))
s))))
(finally
(set! (.-rswapping a) false)))))
nil)
(defn next-tick
"Run f using requestAnimationFrame or equivalent.
f will be called just before components are rendered."
[f]
(batch/do-before-flush f))
(defn after-render
"Run f using requestAnimationFrame or equivalent.
f will be called just after any queued renders in the next animation
frame (and even if no renders actually occur)."
[f]
(batch/do-after-render f))
(defn partial
"Works just like clojure.core/partial, but the result can be compared with ="
[f & args]
(util/make-partial-fn f args))
(defn create-compiler
"Creates Compiler object with given `opts`,
this can be passed to `render`, `as-element` and other functions to control
how they turn the Reagent-style Hiccup into React components and elements."
[opts]
(tmpl/create-compiler opts))
(defn set-default-compiler!
"Globally sets the Compiler object used by `render`, `as-element` and other
calls by default, when no `compiler` parameter is provided.
Use `nil` value to restore the original default compiler."
[compiler]
(tmpl/set-default-compiler! (if (nil? compiler)
tmpl/default-compiler*
compiler)))
(defn render
{:deprecated "0.10.0"}
[& _]
(throw (js/Error. "Reagent.core/render function was moved to day8.re-frame-10x.inlined-deps.reagent.v1v0v0.reagent.dom namespace in Reagent v1.0."))
nil)