Frontend-‐Entwicklung mit
React/Reacl
Michael Sperber
@sperbsen
•
Individualsoftware
•
branchenunabhängig
•
Scala, Clojure, Erlang, Haskell, F#, Swift
•
Schulungen, Coaching
www.active-‐group.de
MVC
Modell
Problem
Modell
View
Modell’
View’
Best of JavaFX
javafx.application.Platform.runLater(
!
new Runnable {
!
override def run() {
!
val webView =
!
new javafx.scene.web.
WebView
()
!
val webEngine = webView.
getEngine
!
val mainURL = getClass.getResource
!
(
”/index.html"
)
!
webEngine.load(mainURL.toExternalForm)
!
Wo läuft JavaScript/HTML5?
•
Browser
•
Mobile
•
JavaFX
•
Windows-‐Apps
•
QT
•
Gtk
Was ist so schlimm an JavaScript?
•
JavaScript
Reacl
ClojureScript
Reacl
Modell
View
neues
Modell
neuer
View
Änderung generiert generiert UserRendering in React
virtuelles DOM
Die allgegenwärtige
Todo-‐App
•
Lisp
•
funktional
Clojure
15
!
true
!
false
!
"foo"
!
(
+
1 2
)!
(
+
1
(
*
2
!
(
+
17 21
)))!
Clojure
(
def
pi
3.14159265
)!
!
(
defn
circumference!
[r]!
(
*
2
pi r))!
Applikations-‐Zustand
(defrecord Todo
[id text done?])
(def t1 (->Todo 0 “Make money” false)
(def t2 (->Todo 1 “retire” false)
(defrecord TodosApp
[next-id todos])
Einzelnes Todo
(
reacl/defclass
to-do-item
!
this todo []
!
render
!
(
dom/div
!
(
dom/input
{
:type
"checkbox"
!
:value
(
:done?
todo)})
!
(
dom/button
"Zap")
!
(
:text
todo)))
!
Todo abhaken
(reacl/defclass to-do-item
!
this todo []
!
render
!
(dom/div
!
(dom/input {
:type
"checkbox"
!
:value
(
:done?
todo)
!
:onChange!
(fn [_] ... )})
!
(dom/button
"Zap"
)
!
(
:text
todo)))
!
Todo abhaken
(reacl/defclass to-do-item!this todo []!
render!
(dom/div!
(dom/input {:type "checkbox"!
:value (:done? todo)!
:onChange! (fn [_]! (reacl/send-message! ! this! ...))})! (dom/button "Zap")! (:text todo)))!
Todo abhaken
(reacl/defclass to-do-item!this todo []!
render!
(dom/div!
(dom/input {:type "checkbox"!
:value (:done? todo)!
:onChange!
(fn [_]!
(reacl/send-message! ! this!
(.-checked (dom/dom-node this ...)))})!
(dom/button "Zap")!
(:text todo)))!
Todo abhaken
(reacl/defclass to-do-item! this todo []! render! (dom/letdom! [checkbox (dom/input! {:type "checkbox"!:value (:done? todo)!
:onChange!
(fn [_]!
(reacl/send-message! !
this!
(.-checked (dom/dom-node this checkbox))))})]!
(dom/div checkbox!
(dom/button "Zap")!
Checkbox-‐Message behandeln
(reacl/defclass to-do-item! this todo []! render! (dom/letdom! [checkbox (dom/input! {:type "checkbox"!:value (:done? todo)!
:onChange!
(fn [_]!
(reacl/send-message! !
this!
(.-checked (dom/dom-node this checkbox))))})]!
(dom/div checkbox! (dom/button "Zap")! (:text todo)))! handle-message! (fn [checked?]! (reacl/return :app-state!
Checkbox-‐Message behandeln
handle-message!
(
fn
[checked?]!
(
reacl/return
!
:app-state
!
(
assoc
todo
:done?
!
checked?)))!
(reacl/send-message! !
this!
Reine Funktionen
id 5
text retire
done? false
(
assoc :done? true
)
id 5
text retire
done? true
Todo löschen
(dom/button
"Zap"
)
Todo löschen
(
defrecord
Delete [todo])!
(
dom/button
!
{
:onClick
!
(
fn
[_]!
(
reacl/send-message!
!
parent (
->Delete
todo)))}!
"Zap"
)!
!
Parent-‐Parameter
(
reacl/defclass
to-do-item!
this todo [parent]
...
)!
Unveränderbare Daten
[
]
Lokaler Zustand
local
state
Applikations-‐Zustand,
lokaler Zustand
(
reacl/defclass
to-do-app!
this app-state local-state []!
initial-state “”!
render!
(
dom/div
!
(
dom/h3
"TODO"
)!
...))!
local
state
app
state
Todo-‐App
(
defrecord
TodosApp
!
[next-id todos])
!
!
(
defrecord
NewText [text])
!
(
defrecord
Submit [])
!
(
defrecord
Change [todo])
!
(
defrecord
Delete [todo])
!
!
!
!
Todo-‐App
(reacl/defclass to-do-app!this app-state local-state []!
render!
(dom/div!
(dom/h3 "TODO")!
(dom/div !
(map (fn [todo]!
(dom/keyed (str (:id todo))!
(to-do-item! todo!
(reacl/reaction this ->Change)!
this)))!
(:todos app-state)))!
Reacl-‐Komponente instanzieren
(
to-do-item
!
todo!
(
reacl/reaction
this ->Change)!
this))
(
reacl/defclass
to-do-item!
this todo [parent]!
...)!
Reaktionen
(
reacl/reaction
this ->Change)!
!
Unveränderbare Daten
[
]
Todo ändern
handle-message! (fn [msg]! (cond! ...! (instance? Change msg)!(let [changed-todo (:todo msg)!
changed-id (:id changed-todo)]!
(reacl/return :app-state!
(assoc app-state!
:todos (map (fn [todo]!
(if (= changed-id (:id todo))!
changed-todo!
todo))!
Neue Todos
(reacl/defclass to-do-app!this app-state local-state []!
render! (dom/div! ...! (dom/form! {:onSubmit (fn [e]! (.preventDefault e)!
(reacl/send-message! this (->Submit)))}!
...!
(dom/button!
Neue Todos
handle-message! (fn [msg]! (cond! ... ! (instance? Submit msg)!(let [next-id (:next-id app-state)]!
(reacl/return :local-state ""!
:app-state!
(assoc app-state!
:todos!
(concat (:todos app-state)!
[(->Todo next-id!
local-state false)])!
Text für neues Todo
(reacl/defclass to-do-app!this app-state local-state []!
render! (dom/div! ...! (dom/form! ...! (dom/input {:onChange ! (fn [e]! (reacl/send-message!! this! (->NewText! (.. e -target -value))))! :value local-state})))))!
Text für neues Todo
handle-message!
(
fn
[msg]!
(
cond
!
...!
(
instance?
NewText msg)!
(
reacl/return
:local-state
!
(
:text
msg))))!
Komponenten-‐Baum
Argumente,
App-‐Zustand
lokaler ZustandArgumente,
App-‐Zustand
App-‐ZustandArgumente,
App-‐Zustand
Message-‐Handler Message-‐Handler Reaktion
React/Reacl
•
JavaFX
•
Design mit HTML5/CSS
•
ClojureScript statt JavaScript
•
[Clojure statt Java]
•
unveränderliche Daten & React statt
DOM-‐Manipulation
Clojure/ClojureScript/Reacl
•
mehr Infos:
Todo löschen
handle-message! (fn [msg]! (cond! ...! (instance? Delete msg)!(let [id (:id (:todo msg))]!
(reacl/return :app-state!
(assoc app-state!
:todos !
(remove (fn [todo] (= id (:id todo)))!
(:todos app-state)))))!