ClojureScript	
  	
  	
  	
  	
  	
  	
  	
  	
  ReactJS	
  
Michiel	
  Borkent	
  	
  
@borkdude	
  
DomCode,	
  May	
  26th	
  2015	
  
Michiel	
  Borkent	
  (@borkdude)	
  
	
  
●  Clojure(Script)	
  developer	
  at	
  
●  Clojure	
  since	
  2009	
  
●  Former	
  lecturer,	
  taught	
  Clojure	
  
Agenda	
  
●  Part	
  1:	
  ClojureScript	
  
●  Part	
  2:	
  ClojureScript	
  	
  	
  	
  	
  	
  	
  ReactJS	
  
Warning	
  
Part	
  1:	
  ClojureScript	
  
Current	
  status	
  
●  JavaScript	
  is	
  everywhere,	
  but	
  not	
  a	
  robust	
  and	
  concise	
  
language	
  -­‐	
  wat	
  	
  
Requires	
  discipline	
  to	
  only	
  use	
  "the	
  good	
  parts"	
  
●  JavaScript	
  is	
  taking	
  over:	
  UI	
  logic	
  from	
  server	
  to	
  client	
  
●  JavaScript	
  is	
  not	
  going	
  away	
  in	
  the	
  near	
  future	
  
●  Advanced	
  libraries	
  and	
  technologies	
  exist	
  to	
  opTmize	
  
JavaScript:	
  (example:	
  Google	
  Closure)	
  
	
  
ClojureScript	
  
●  Released	
  June	
  20th	
  2011	
  
●  Client	
  side	
  story	
  of	
  Clojure	
  ecosystem	
  
●  Serves	
  Clojure	
  community:	
  
50%*	
  of	
  Clojure	
  users	
  also	
  use	
  ClojureScript	
  
93%**	
  of	
  ClojureScript	
  users	
  also	
  use	
  Clojure	
  
●  ClojureScript	
  targets	
  JavaScript	
  by	
  adopTng	
  Google	
  Closure	
  
o  libraries:	
  goog.provide/require	
  etc.	
  
o  opTmizaTon:	
  dead	
  code	
  removal	
  
*h[p://cemerick.com/2013/11/18/results-­‐of-­‐the-­‐2013-­‐state-­‐of-­‐clojure-­‐clojurescript-­‐survey/	
  	
  
**	
  h[p://blog.cognitect.com/blog/2014/10/24/analysis-­‐of-­‐the-­‐state-­‐of-­‐clojure-­‐and-­‐clojurescript-­‐survey-­‐2014	
  	
  
f(x)	
  -­‐>	
  (f	
  x)	
  
	
  
Syntax	
  
Syntax	
  
if	
  (...)	
  {	
  
	
  	
  ...	
  
}	
  else	
  {	
  	
  	
  	
  	
  	
  	
  -­‐>	
  
	
  	
  ...	
  	
  
}	
  
	
  
(if	
  ...	
  
	
  	
  	
  	
  ...	
  
	
  	
  	
  	
  ...)	
  
Syntax	
  
var	
  foo	
  =	
  "bar";	
  	
  
	
  
(def	
  foo	
  "bar")	
  
JavaScript	
  -­‐	
  ClojureScript	
  
//	
  In	
  JavaScript	
  
//	
  locals	
  are	
  mutable	
  
	
  	
  
function	
  foo(x)	
  {	
  
	
  	
  x	
  =	
  "bar";	
  
}	
  
;;	
  this	
  will	
  issue	
  an	
  
;;	
  error	
  
	
  	
  
(defn	
  foo	
  [x]	
  
	
  	
  (set!	
  x	
  "bar"))	
  
source:	
  h[p://himera.herokuapp.com/synonym.html	
  
JavaScript	
  -­‐	
  ClojureScript	
  
if	
  (bugs.length	
  >	
  0)	
  {	
  
	
  	
  return	
  'Not	
  ready	
  for	
  release';	
  
}	
  else	
  {	
  
	
  	
  return	
  'Ready	
  for	
  release';	
  
}	
  
	
  
(if	
  (pos?	
  (count	
  bugs))	
  
	
  	
  "Not	
  ready	
  for	
  release"	
  
	
  	
  "Ready	
  for	
  release")	
  
source:	
  h[p://himera.herokuapp.com/synonym.html	
  
JavaScript	
  -­‐	
  ClojureScript	
  
var	
  foo	
  =	
  {bar:	
  "baz"};	
  
foo.bar	
  =	
  "baz";	
  
foo["abc"]	
  =	
  17;	
  
	
  
alert('foo')	
  
new	
  Date().getTime()	
  
new	
  Date().getTime().toString()	
  
	
  
	
  
(def	
  foo	
  (js-­‐obj	
  "bar"	
  "baz"))	
  
(set!	
  (.-­‐bar	
  foo)	
  "baz")	
  
(aset	
  foo	
  "abc"	
  17)	
  
	
  
(js/alert	
  "foo")	
  
(.getTime	
  (js/Date.))	
  
(..	
  (js/Date.)	
  (getTime)	
  (toString))	
  
source:	
  h[p://himera.herokuapp.com/synonym.html	
  
Core	
  language	
  features	
  
●  persistent	
  immutable	
  data	
  structures	
  
●  funcTonal	
  programming	
  
●  sequence	
  abstracTon	
  
●  isolaTon	
  of	
  mutable	
  state	
  (atoms)	
  
●  Lisp:	
  macros,	
  REPL	
  
●  core.async	
  
Persistent	
  data	
  structures	
  
(def	
  v	
  [1	
  2	
  3])	
  
(conj	
  v	
  4)	
  ;;	
  =>	
  [1	
  2	
  3	
  4]	
  
(get	
  v	
  0)	
  ;;	
  =>	
  1	
  
(v	
  0)	
  ;;	
  =>	
  1	
  
	
  
source:	
  h[p://hypirion.com/musings/understanding-­‐persistent-­‐vector-­‐pt-­‐1	
  	
  
Persistent	
  data	
  structures	
  
(def	
  m	
  {:foo	
  1	
  :bar	
  2})	
  
(assoc	
  m	
  :foo	
  2)	
  ;;	
  =>	
  {:foo	
  2	
  :bar	
  2}	
  
(get	
  m	
  :foo)	
  ;;=>	
  1	
  
(m	
  :foo)	
  ;;=>	
  1	
  
(:foo	
  m)	
  ;;=>	
  1	
  
(dissoc	
  m	
  :foo)	
  ;;=>	
  {:bar	
  2}	
  
FuncFonal	
  programming	
  
(def	
  r	
  (-­‐>>	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  (range	
  10)	
  	
  	
  	
  ;;	
  (0	
  1	
  2	
  ..	
  9)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  (filter	
  odd?)	
  ;;	
  (1	
  3	
  5	
  7	
  9)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  (map	
  inc)))	
  	
  	
  ;;	
  (2	
  4	
  6	
  8	
  10)	
  	
  
;;	
  r	
  is	
  (2	
  4	
  6	
  8	
  10)	
  
	
  
FuncFonal	
  programming	
  
;;	
  r	
  is	
  (2	
  4	
  6	
  8	
  10)	
  
(reduce	
  +	
  r)	
  	
  
;;	
  =>	
  30	
  
(reductions	
  +	
  r)	
  
;;	
  =>	
  (2	
  6	
  12	
  20	
  30)	
  
	
  
	
  
	
  
var	
  sum	
  =	
  _.reduce(r,	
  function(memo,	
  num){	
  return	
  memo	
  +	
  num;	
  });	
  
Sequence	
  abstracFon	
  
Data	
  structures	
  as	
  seqs	
  
(first	
  [1	
  2	
  3])	
  ;;=>	
  1	
  
(rest	
  [1	
  2	
  3])	
  ;;=>	
  (2	
  3)	
  
General	
  seq	
  funcTons:	
  map,	
  reduce,	
  filter,	
  ...	
  
(distinct	
  [1	
  1	
  2	
  3])	
  ;;=>	
  (1	
  2	
  3)	
  
(take	
  2	
  (range	
  10))	
  ;;=>	
  (0	
  1)	
  
	
  
See	
  h[p://clojure.org/cheatsheet	
  for	
  more	
  	
  
Sequence	
  abstracFon	
  
Most	
  seq	
  funcTons	
  return	
  lazy	
  sequences:	
  
	
  
(take	
  2	
  (map	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (fn	
  [n]	
  (js/alert	
  n)	
  n)	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (range)))	
  
	
   infinite	
  lazy	
  sequence	
  of	
  numbers	
  
side	
  effect	
  
Mutable	
  state:	
  atoms	
  
(def	
  my-­‐atom	
  (atom	
  0))	
  
@my-­‐atom	
  ;;	
  0	
  
(reset!	
  my-­‐atom	
  1)	
  
(reset!	
  my-­‐atom	
  (inc	
  @my-­‐atom))	
  ;;	
  bad	
  idiom	
  
(swap!	
  my-­‐atom	
  (fn	
  [old-­‐value]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (inc	
  old-­‐value)))	
  
(swap!	
  my-­‐atom	
  inc)	
  ;;	
  same	
  
@my-­‐atom	
  ;;	
  4	
  
	
  
IsolaFon	
  of	
  state	
  
adapted	
  from:	
  h[ps://github.com/dfuenzalida/todo-­‐cljs	
  	
  
one	
  of	
  possible	
  
pre-­‐React	
  pa[erns	
  
funcTon	
  	
  called	
  
from	
  event	
  
handler	
  
(def	
  app-­‐state	
  (atom	
  []))	
  	
  
	
  
(declare	
  rerender)	
  
	
  
(add-­‐watch	
  app-­‐state	
  ::rerender	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (fn	
  [k	
  a	
  o	
  n]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (rerender	
  o	
  n)))	
  
	
  
(defn	
  add-­‐todo	
  [text]	
  
	
  	
  (let	
  [tt	
  (.trim	
  text)]	
  
	
  	
  	
  	
  (if	
  (seq	
  tt)	
  
	
  	
  	
  	
  	
  	
  (swap!	
  app-­‐state	
  conj	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:id	
  (get-­‐uuid)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :title	
  tt	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :completed	
  false}))))	
  
	
  
new	
  todo	
  
Lisp:	
  macros	
  
(map	
  inc	
  	
  
	
  	
  (filter	
  odd?	
  	
  
	
  	
  	
  	
  (range	
  10))))	
  
	
  
(-­‐>>	
  	
  
	
  	
  (range	
  10)	
  
	
  	
  (filter	
  odd?)	
  
	
  	
  (map	
  inc))	
  
thread	
  last	
  macro	
  
Lisp:	
  macros	
  
(macroexpand	
  	
  
	
  	
  '(-­‐>>	
  (range	
  10)	
  (filter	
  odd?)))	
  
	
  
;;	
  =>	
  (filter	
  odd?	
  (range	
  10))	
  
	
  
(macroexpand	
  	
  
	
  	
  '(-­‐>>	
  (range	
  10)	
  (filter	
  odd?)	
  (map	
  inc)))	
  
	
  
;;	
  =>	
  (map	
  inc	
  (filter	
  odd?	
  (range	
  10)))	
  
	
  
Lisp:	
  macros	
  
JVM	
  Clojure:	
  
	
  
(defmacro	
  defonce	
  [x	
  init]	
  
	
  `(when-­‐not	
  (exists?	
  ~x)	
  
	
  	
  	
  	
  (def	
  ~x	
  ~init)))	
  
	
  
	
  
ClojureScript:	
  
	
  
(defonce	
  foo	
  1)	
  
(defonce	
  foo	
  2)	
  ;;	
  no	
  effect	
  
notes:	
  	
  
●  macros	
  must	
  be	
  wri[en	
  in	
  JVM	
  Clojure	
  
●  are	
  expanded	
  at	
  compile	
  Tme	
  
●  generated	
  code	
  gets	
  executes	
  in	
  ClojureScript	
  
core.async	
  
(go	
  (let	
  [email	
  (:body	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (<!	
  (http/get	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (str	
  "/api/users/"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "123"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "/email"))))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  orders	
  (:body	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (<!	
  (http/get	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (str	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "/api/orders-­‐by-­‐email/"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  email))))]	
  
	
  	
  	
  	
  	
  	
  (count	
  orders)))	
  
Part	
  2:	
  
React	
  	
  
●  Developed	
  by	
  Facebook	
  
●  Helps	
  building	
  reusable	
  and	
  composable	
  UI	
  components	
  
●  Leverages	
  virtual	
  DOM	
  for	
  performance	
  
●  Can	
  render	
  on	
  server	
  to	
  make	
  apps	
  crawlable	
  	
  
●  JSX	
  templaTng	
  language
	
  
var	
  Counter	
  =	
  React.createClass({	
  
	
  	
  	
  	
  getInitialState:	
  function()	
  {	
  
	
  	
  	
  	
  	
  	
  return	
  {counter:	
  this.props.initialCount};	
  
	
  	
  	
  	
  },	
  
	
  	
  	
  	
  inc:	
  function()	
  {	
  
	
  	
  	
  	
  	
  	
  this.setState({counter:	
  this.state.counter	
  +	
  1});	
  
	
  	
  	
  	
  },	
  
	
  	
  	
  	
  render:	
  function()	
  {	
  
	
  	
  	
  	
  	
  	
  	
  	
  return	
  <div>	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {this.state.counter}	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  <button	
  onClick={this.inc}>x</button>	
  	
  	
  
	
  	
  	
  	
  	
  	
  	
  	
  </div>;	
  
	
  	
  	
  	
  }	
  
});	
  
	
  
React.renderComponent(<Counter	
  initialCount={10}/>,	
  document.body);	
  
	
  
Reagent	
  
ClojureScript	
  interface	
  to	
  React	
  
•  Uses	
  special	
  atoms	
  for	
  state	
  
•  Data	
  literals	
  for	
  templaTng	
  
•  Uses	
  batching	
  +	
  more	
  efficient	
  shouldComponentUpdate	
  
	
  
Components	
  are	
  funcFons	
  that	
  	
  
•  must	
  return	
  something	
  renderable	
  by	
  React	
  	
  
•  can	
  deref	
  atom(s)	
  
•  can	
  accept	
  props	
  as	
  args	
  
•  may	
  return	
  a	
  closure,	
  useful	
  for	
  seing	
  up	
  iniTal	
  state	
  
	
  
Data	
  literals	
  
Symbol:	
  	
  	
  	
  	
  :a	
  
Vector:	
  	
  	
  	
  	
  [1	
  2	
  3	
  4]	
  
Hash	
  map:	
  	
  	
  {:a	
  1,	
  :b	
  2}	
  
Set:	
  	
  	
  	
  	
  	
  	
  	
  #{1	
  2	
  3	
  4}	
  
List:	
  	
  	
  	
  	
  	
  	
  '(1	
  2	
  3	
  4)	
  
Hiccup	
  syntax	
  
[:a	
  {:href	
  "/logout"}	
  
	
  	
  "Logout"]	
  
	
  
[:div#app.container	
  
	
  	
  [:h2	
  "Welcome"]]	
  
	
  	
  	
  	
  
<a	
  href="/logout">Logout</a>	
  
	
  
	
  
	
  
<div	
  id="app"	
  class="container">	
  
	
  	
  <h2>Welcome</h2>	
  
</div>	
  
(def	
  count-­‐state	
  (atom	
  10))	
  
	
  
(defn	
  counter	
  []	
  
	
  	
  [:div	
  
	
  	
  	
  @count-­‐state	
  
	
  	
  	
  [:button	
  {:on-­‐click	
  (fn	
  []	
  (swap!	
  count-­‐state	
  inc))}	
  
	
  	
  	
  	
  "x"]])	
  
	
  
(reagent/render-­‐component	
  [counter]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (js/document.getElementById	
  "app"))	
  
	
  
	
  
	
  
	
  
	
  
	
  
RAtom
(defn	
  local-­‐counter	
  [start-­‐value]	
  
	
  	
  (let	
  [count-­‐state	
  (atom	
  start-­‐value)]	
  
	
  	
  	
  	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  [:div	
  
	
  	
  	
  	
  	
  	
  	
  	
  @count-­‐state	
  
	
  	
  	
  	
  	
  	
  	
  	
  [:button	
  {:on-­‐click	
  #(swap!	
  count-­‐state	
  inc)}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "x"]])))	
  
	
  
(reagent/render-­‐component	
  [local-­‐counter	
  10]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (js/document.getElementById	
  "app"))	
  
	
  
	
  
	
  
	
  
	
  
	
  
	
  
	
  
local
RAtom
CRUD!	
  
(def	
  Animals	
  
	
  	
  "A	
  schema	
  for	
  animals	
  state"	
  
	
  	
  #{{:id	
  	
  	
  	
  	
  	
  s/Int	
  
	
  	
  	
  	
  	
  :type	
  	
  	
  	
  s/Keyword	
  
	
  	
  	
  	
  	
  :name	
  	
  	
  	
  s/Str	
  
	
  	
  	
  	
  	
  :species	
  s/Str}})	
  
	
  
(defonce	
  animals-­‐state	
  
	
  	
  (atom	
  #{}	
  
	
  	
  	
  	
  	
  	
  	
  	
  :validator	
  
	
  	
  	
  	
  	
  	
  	
  	
  (fn	
  [n]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (s/validate	
  Animals	
  n))))	
  
	
  
;;	
  initial	
  call	
  to	
  get	
  animals	
  from	
  server	
  
(go	
  (let	
  [response	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (<!	
  (http/get	
  "/animals"))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  data	
  (:body	
  response)]	
  
	
  	
  	
  	
  	
  	
  (reset!	
  animals-­‐state	
  (set	
  data))))	
  
	
  
RAtom with set containing
animal hash-maps
(...	
  	
  
	
  {:id	
  2,	
  
	
  	
  :type	
  :animal,	
  
	
  	
  :name	
  "Yellow-­‐backed	
  duiker",	
  
	
  	
  :species	
  "Cephalophus	
  silvicultor"}	
  
	
  {:id	
  1,	
  
	
  	
  :type	
  :animal,	
  
	
  	
  :name	
  "Painted-­‐snipe",	
  
	
  	
  :species	
  "Rostratulidae"}	
  
Render	
  all	
  animals	
  from	
  state	
  
(defn	
  animals	
  []	
  
	
  	
  [:div	
  
	
  	
  	
  [:table.table.table-­‐striped	
  
	
  	
  	
  	
  [:thead	
  
	
  	
  	
  	
  	
  [:tr	
  
	
  	
  	
  	
  	
  	
  [:th	
  "Name"]	
  [:th	
  "Species"]	
  [:th	
  ""]	
  [:th	
  ""]]]	
  
	
  	
  	
  	
  [:tbody	
  
	
  	
  	
  	
  	
  (map	
  (fn	
  [a]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  ^{:key	
  (str	
  "animal-­‐row-­‐"	
  (:id	
  a))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  [animal-­‐row	
  a])	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (sort-­‐by	
  :name	
  @animals-­‐state))	
  
	
  	
  	
  	
  	
  [animal-­‐form]]]])	
  
	
  
animal-­‐row	
  component	
  
{:editing?	
  false,	
  :name	
  "Yellow-­‐backed	
  duiker”,	
  :species	
  "Cephalophus	
  silvicultor"}	
  
{:editing?	
  true,	
  :name	
  "Yellow-­‐backed	
  pony”,	
  :species	
  "Cephalophus	
  silvicultor"}	
  
(defn	
  animal-­‐row	
  [a]	
  
	
  	
  (let	
  [row-­‐state	
  (atom	
  {:editing?	
  false	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  	
  	
  	
  	
  (:name	
  a)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  	
  (:species	
  a)})	
  
	
  	
  	
  	
  	
  	
  	
  	
  current-­‐animal	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (assoc	
  a	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  (:name	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  (:species	
  @row-­‐state)))]	
  
	
  	
  	
  	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  [:tr	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :name]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :species]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.btn-­‐primary.pull-­‐right	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:disabled	
  (not	
  (input-­‐valid?	
  row-­‐state))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :on-­‐click	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (when	
  (:editing?	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (update-­‐animal!	
  (current-­‐animal)))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  row-­‐state	
  update-­‐in	
  [:editing?]	
  not))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (if	
  (:editing?	
  @row-­‐state)	
  "Save"	
  "Edit")]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.pull-­‐right.btn-­‐danger	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:on-­‐click	
  #(remove-­‐animal!	
  (current-­‐animal))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "u00D7"]]])))	
  
	
  
(defn	
  editable-­‐input	
  [atom	
  key]	
  
	
  	
  (if	
  (:editing?	
  @atom)	
  
	
  	
  	
  	
  [:input	
  {:type	
  	
  	
  	
  	
  "text"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :value	
  	
  	
  	
  (get	
  @atom	
  key)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :on-­‐change	
  (fn	
  [e]	
  (swap!	
  atom	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  assoc	
  key	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (..	
  e	
  -­‐target	
  -­‐value)))}]	
  
	
  	
  	
  	
  [:p	
  (get	
  @atom	
  key)]))	
  
	
  
{:editing?	
  false,	
  :name	
  "Yellow-­‐backed	
  duiker",	
  :species	
  "Cephalophus	
  silvicultor"}	
  
{:editing?	
  true,	
  :name	
  "Yellow-­‐backed	
  pony",	
  :species	
  "Cephalophus	
  silvicultor"}	
  
(defn	
  animal-­‐row	
  [a]	
  
	
  	
  (let	
  [row-­‐state	
  (atom	
  {:editing?	
  false	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  	
  	
  	
  	
  (:name	
  a)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  	
  (:species	
  a)})	
  
	
  	
  	
  	
  	
  	
  	
  	
  current-­‐animal	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (assoc	
  a	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  (:name	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  (:species	
  @row-­‐state)))]	
  
	
  	
  	
  	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  [:tr	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :name]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :species]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.btn-­‐primary.pull-­‐right	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:disabled	
  (not	
  (input-­‐valid?	
  row-­‐state))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :on-­‐click	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (when	
  (:editing?	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (update-­‐animal!	
  (current-­‐animal)))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  row-­‐state	
  update-­‐in	
  [:editing?]	
  not))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (if	
  (:editing?	
  @row-­‐state)	
  "Save"	
  "Edit")]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.pull-­‐right.btn-­‐danger	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:on-­‐click	
  #(remove-­‐animal!	
  (current-­‐animal))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "u00D7"]]])))	
  
	
  
(defn	
  input-­‐valid?	
  [atom]	
  
	
  	
  (and	
  (seq	
  (-­‐>	
  @atom	
  :name))	
  
	
  	
  	
  	
  	
  	
  	
  (seq	
  (-­‐>	
  @atom	
  :species))))	
  
	
  
(defn	
  animal-­‐row	
  [a]	
  
	
  	
  (let	
  [row-­‐state	
  (atom	
  {:editing?	
  false	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  	
  	
  	
  	
  (:name	
  a)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  	
  (:species	
  a)})	
  
	
  	
  	
  	
  	
  	
  	
  	
  current-­‐animal	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (assoc	
  a	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  (:name	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  (:species	
  @row-­‐state)))]	
  
	
  	
  	
  	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  [:tr	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :name]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :species]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.btn-­‐primary.pull-­‐right	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:disabled	
  (not	
  (input-­‐valid?	
  row-­‐state))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :on-­‐click	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (when	
  (:editing?	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (update-­‐animal!	
  (current-­‐animal)))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  row-­‐state	
  update-­‐in	
  [:editing?]	
  not))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (if	
  (:editing?	
  @row-­‐state)	
  "Save"	
  "Edit")]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.pull-­‐right.btn-­‐danger	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:on-­‐click	
  #(remove-­‐animal!	
  (current-­‐animal))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "u00D7"]]])))	
  
	
  
(defn	
  update-­‐animal!	
  [a]	
  
	
  	
  (go	
  (let	
  [response	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (<!	
  (http/put	
  (str	
  "/animals/"	
  (:id	
  a))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:edn-­‐params	
  a}))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  updated-­‐animal	
  (:body	
  response)]	
  
	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  animals-­‐state	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (fn	
  [old-­‐state]	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (conj	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (remove-­‐by-­‐id	
  old-­‐state	
  (:id	
  a))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  updated-­‐animal))))))	
  
	
  
(defn	
  animal-­‐row	
  [a]	
  
	
  	
  (let	
  [row-­‐state	
  (atom	
  {:editing?	
  false	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  	
  	
  	
  	
  (:name	
  a)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  	
  (:species	
  a)})	
  
	
  	
  	
  	
  	
  	
  	
  	
  current-­‐animal	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (assoc	
  a	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :name	
  (:name	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :species	
  (:species	
  @row-­‐state)))]	
  
	
  	
  	
  	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  [:tr	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :name]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [editable-­‐input	
  row-­‐state	
  :species]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.btn-­‐primary.pull-­‐right	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:disabled	
  (not	
  (input-­‐valid?	
  row-­‐state))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  :on-­‐click	
  (fn	
  []	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (when	
  (:editing?	
  @row-­‐state)	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (update-­‐animal!	
  (current-­‐animal)))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  row-­‐state	
  update-­‐in	
  [:editing?]	
  not))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (if	
  (:editing?	
  @row-­‐state)	
  "Save"	
  "Edit")]]	
  
	
  	
  	
  	
  	
  	
  	
  [:td	
  [:button.btn.pull-­‐right.btn-­‐danger	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  {:on-­‐click	
  #(remove-­‐animal!	
  (current-­‐animal))}	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  "u00D7"]]])))	
  
	
  
(defn	
  remove-­‐animal!	
  [a]	
  
	
  	
  (go	
  (let	
  [response	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (<!	
  (http/delete	
  (str	
  "/animals/"	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (:id	
  a))))]	
  
	
  	
  	
  	
  	
  	
  	
  	
  (if	
  (=	
  200	
  (:status	
  response))	
  
	
  	
  	
  	
  	
  	
  	
  	
  	
  	
  (swap!	
  animals-­‐state	
  remove-­‐by-­‐id	
  (:id	
  a))))))	
  
	
  
	
  
if server says:
"OK!", remove
animal from
CRUD table
Exercises	
  
-­‐  Sort	
  table	
  by	
  clicking	
  on	
  name	
  or	
  species	
  
-­‐  OpTmisTc	
  updates	
  
Code	
  and	
  slides	
  at:	
  
h[ps://github.com/borkdude/domcode-­‐cljs-­‐react	
  	
  
•  Install	
  JDK	
  7+	
  
•  Install	
  leiningen	
  (build	
  tool)	
  
•  git	
  clone	
  https://github.com/borkdude/domcode-­‐cljs-­‐react.git	
  	
  
•  cd	
  domcode-­‐cljs-­‐react/code/animals-­‐crud	
  
•  See	
  README.md	
  for	
  further	
  instrucTons	
  
Probably	
  Cursive	
  IDE	
  (IntelliJ)	
  is	
  most	
  beginner	
  friendly	
  
	
  
	
  	
  
	
  
	
  
How	
  to	
  run	
  at	
  home?	
  
Leiningen	
  
●  Used	
  by	
  98%	
  of	
  Clojure	
  users	
  
●  Clojure's	
  Maven	
  
●  Managing	
  dependencies	
  
●  Running	
  a	
  REPL	
  
●  Packaging	
  and	
  deploying	
  
●  Plugins:	
  
○  lein	
  cljsbuild	
  –	
  building	
  ClojureScript	
  
○  lein	
  figwheel	
  –	
  live	
  code	
  reloading	
  in	
  
browser	
  
Debugging	
  
Source	
  maps	
  let	
  you	
  debug	
  ClojureScript	
  directly	
  from	
  the	
  browser	
  
	
  
Get	
  started	
  with	
  Clojure(Script)	
  
•  Read	
  a	
  Clojure(Script)	
  book	
  
•  Do	
  the	
  4clojure	
  exercises	
  
•  Start	
  hacking	
  on	
  your	
  own	
  project	
  
•  Pick	
  an	
  online	
  Clojure	
  course 	
  	
  
•  Join	
  the	
  AMSCLJ	
  meetup	
  
•  Join	
  the	
  Slack	
  community	
  
Thanks!	
  

ClojureScript loves React, DomCode May 26 2015

  • 1.
    ClojureScript                  ReactJS   Michiel  Borkent     @borkdude   DomCode,  May  26th  2015  
  • 2.
    Michiel  Borkent  (@borkdude)     ●  Clojure(Script)  developer  at   ●  Clojure  since  2009   ●  Former  lecturer,  taught  Clojure  
  • 3.
    Agenda   ●  Part  1:  ClojureScript   ●  Part  2:  ClojureScript              ReactJS  
  • 4.
  • 5.
  • 6.
    Current  status   ● JavaScript  is  everywhere,  but  not  a  robust  and  concise   language  -­‐  wat     Requires  discipline  to  only  use  "the  good  parts"   ●  JavaScript  is  taking  over:  UI  logic  from  server  to  client   ●  JavaScript  is  not  going  away  in  the  near  future   ●  Advanced  libraries  and  technologies  exist  to  opTmize   JavaScript:  (example:  Google  Closure)    
  • 7.
    ClojureScript   ●  Released  June  20th  2011   ●  Client  side  story  of  Clojure  ecosystem   ●  Serves  Clojure  community:   50%*  of  Clojure  users  also  use  ClojureScript   93%**  of  ClojureScript  users  also  use  Clojure   ●  ClojureScript  targets  JavaScript  by  adopTng  Google  Closure   o  libraries:  goog.provide/require  etc.   o  opTmizaTon:  dead  code  removal   *h[p://cemerick.com/2013/11/18/results-­‐of-­‐the-­‐2013-­‐state-­‐of-­‐clojure-­‐clojurescript-­‐survey/     **  h[p://blog.cognitect.com/blog/2014/10/24/analysis-­‐of-­‐the-­‐state-­‐of-­‐clojure-­‐and-­‐clojurescript-­‐survey-­‐2014    
  • 8.
    f(x)  -­‐>  (f  x)     Syntax  
  • 9.
    Syntax   if  (...)  {      ...   }  else  {              -­‐>      ...     }     (if  ...          ...          ...)  
  • 10.
    Syntax   var  foo  =  "bar";       (def  foo  "bar")  
  • 11.
    JavaScript  -­‐  ClojureScript   //  In  JavaScript   //  locals  are  mutable       function  foo(x)  {      x  =  "bar";   }   ;;  this  will  issue  an   ;;  error       (defn  foo  [x]      (set!  x  "bar"))   source:  h[p://himera.herokuapp.com/synonym.html  
  • 12.
    JavaScript  -­‐  ClojureScript   if  (bugs.length  >  0)  {      return  'Not  ready  for  release';   }  else  {      return  'Ready  for  release';   }     (if  (pos?  (count  bugs))      "Not  ready  for  release"      "Ready  for  release")   source:  h[p://himera.herokuapp.com/synonym.html  
  • 13.
    JavaScript  -­‐  ClojureScript   var  foo  =  {bar:  "baz"};   foo.bar  =  "baz";   foo["abc"]  =  17;     alert('foo')   new  Date().getTime()   new  Date().getTime().toString()       (def  foo  (js-­‐obj  "bar"  "baz"))   (set!  (.-­‐bar  foo)  "baz")   (aset  foo  "abc"  17)     (js/alert  "foo")   (.getTime  (js/Date.))   (..  (js/Date.)  (getTime)  (toString))   source:  h[p://himera.herokuapp.com/synonym.html  
  • 14.
    Core  language  features   ●  persistent  immutable  data  structures   ●  funcTonal  programming   ●  sequence  abstracTon   ●  isolaTon  of  mutable  state  (atoms)   ●  Lisp:  macros,  REPL   ●  core.async  
  • 15.
    Persistent  data  structures   (def  v  [1  2  3])   (conj  v  4)  ;;  =>  [1  2  3  4]   (get  v  0)  ;;  =>  1   (v  0)  ;;  =>  1    
  • 16.
  • 17.
    Persistent  data  structures   (def  m  {:foo  1  :bar  2})   (assoc  m  :foo  2)  ;;  =>  {:foo  2  :bar  2}   (get  m  :foo)  ;;=>  1   (m  :foo)  ;;=>  1   (:foo  m)  ;;=>  1   (dissoc  m  :foo)  ;;=>  {:bar  2}  
  • 18.
    FuncFonal  programming   (def  r  (-­‐>>                      (range  10)        ;;  (0  1  2  ..  9)                    (filter  odd?)  ;;  (1  3  5  7  9)                    (map  inc)))      ;;  (2  4  6  8  10)     ;;  r  is  (2  4  6  8  10)    
  • 19.
    FuncFonal  programming   ;;  r  is  (2  4  6  8  10)   (reduce  +  r)     ;;  =>  30   (reductions  +  r)   ;;  =>  (2  6  12  20  30)         var  sum  =  _.reduce(r,  function(memo,  num){  return  memo  +  num;  });  
  • 20.
    Sequence  abstracFon   Data  structures  as  seqs   (first  [1  2  3])  ;;=>  1   (rest  [1  2  3])  ;;=>  (2  3)   General  seq  funcTons:  map,  reduce,  filter,  ...   (distinct  [1  1  2  3])  ;;=>  (1  2  3)   (take  2  (range  10))  ;;=>  (0  1)     See  h[p://clojure.org/cheatsheet  for  more    
  • 21.
    Sequence  abstracFon   Most  seq  funcTons  return  lazy  sequences:     (take  2  (map                        (fn  [n]  (js/alert  n)  n)                            (range)))     infinite  lazy  sequence  of  numbers   side  effect  
  • 22.
    Mutable  state:  atoms   (def  my-­‐atom  (atom  0))   @my-­‐atom  ;;  0   (reset!  my-­‐atom  1)   (reset!  my-­‐atom  (inc  @my-­‐atom))  ;;  bad  idiom   (swap!  my-­‐atom  (fn  [old-­‐value]                                      (inc  old-­‐value)))   (swap!  my-­‐atom  inc)  ;;  same   @my-­‐atom  ;;  4    
  • 23.
    IsolaFon  of  state   adapted  from:  h[ps://github.com/dfuenzalida/todo-­‐cljs     one  of  possible   pre-­‐React  pa[erns   funcTon    called   from  event   handler   (def  app-­‐state  (atom  []))       (declare  rerender)     (add-­‐watch  app-­‐state  ::rerender                        (fn  [k  a  o  n]                            (rerender  o  n)))     (defn  add-­‐todo  [text]      (let  [tt  (.trim  text)]          (if  (seq  tt)              (swap!  app-­‐state  conj                            {:id  (get-­‐uuid)                              :title  tt                              :completed  false}))))     new  todo  
  • 24.
    Lisp:  macros   (map  inc        (filter  odd?            (range  10))))     (-­‐>>        (range  10)      (filter  odd?)      (map  inc))   thread  last  macro  
  • 25.
    Lisp:  macros   (macroexpand        '(-­‐>>  (range  10)  (filter  odd?)))     ;;  =>  (filter  odd?  (range  10))     (macroexpand        '(-­‐>>  (range  10)  (filter  odd?)  (map  inc)))     ;;  =>  (map  inc  (filter  odd?  (range  10)))    
  • 26.
    Lisp:  macros   JVM  Clojure:     (defmacro  defonce  [x  init]    `(when-­‐not  (exists?  ~x)          (def  ~x  ~init)))       ClojureScript:     (defonce  foo  1)   (defonce  foo  2)  ;;  no  effect   notes:     ●  macros  must  be  wri[en  in  JVM  Clojure   ●  are  expanded  at  compile  Tme   ●  generated  code  gets  executes  in  ClojureScript  
  • 27.
    core.async   (go  (let  [email  (:body                                    (<!  (http/get                                              (str  "/api/users/"                                                        "123"                                                        "/email"))))                      orders  (:body                                      (<!  (http/get                                                (str                                                  "/api/orders-­‐by-­‐email/"                                                  email))))]              (count  orders)))  
  • 28.
  • 29.
    React     ● Developed  by  Facebook   ●  Helps  building  reusable  and  composable  UI  components   ●  Leverages  virtual  DOM  for  performance   ●  Can  render  on  server  to  make  apps  crawlable     ●  JSX  templaTng  language  
  • 30.
    var  Counter  =  React.createClass({          getInitialState:  function()  {              return  {counter:  this.props.initialCount};          },          inc:  function()  {              this.setState({counter:  this.state.counter  +  1});          },          render:  function()  {                  return  <div>                      {this.state.counter}                        <button  onClick={this.inc}>x</button>                      </div>;          }   });     React.renderComponent(<Counter  initialCount={10}/>,  document.body);    
  • 31.
    Reagent   ClojureScript  interface  to  React   •  Uses  special  atoms  for  state   •  Data  literals  for  templaTng   •  Uses  batching  +  more  efficient  shouldComponentUpdate     Components  are  funcFons  that     •  must  return  something  renderable  by  React     •  can  deref  atom(s)   •  can  accept  props  as  args   •  may  return  a  closure,  useful  for  seing  up  iniTal  state    
  • 32.
    Data  literals   Symbol:          :a   Vector:          [1  2  3  4]   Hash  map:      {:a  1,  :b  2}   Set:                #{1  2  3  4}   List:              '(1  2  3  4)  
  • 33.
    Hiccup  syntax   [:a  {:href  "/logout"}      "Logout"]     [:div#app.container      [:h2  "Welcome"]]           <a  href="/logout">Logout</a>         <div  id="app"  class="container">      <h2>Welcome</h2>   </div>  
  • 34.
    (def  count-­‐state  (atom  10))     (defn  counter  []      [:div        @count-­‐state        [:button  {:on-­‐click  (fn  []  (swap!  count-­‐state  inc))}          "x"]])     (reagent/render-­‐component  [counter]                                                      (js/document.getElementById  "app"))               RAtom
  • 35.
    (defn  local-­‐counter  [start-­‐value]      (let  [count-­‐state  (atom  start-­‐value)]          (fn  []              [:div                  @count-­‐state                  [:button  {:on-­‐click  #(swap!  count-­‐state  inc)}                      "x"]])))     (reagent/render-­‐component  [local-­‐counter  10]                                                      (js/document.getElementById  "app"))                   local RAtom
  • 36.
  • 37.
    (def  Animals      "A  schema  for  animals  state"      #{{:id            s/Int            :type        s/Keyword            :name        s/Str            :species  s/Str}})     (defonce  animals-­‐state      (atom  #{}                  :validator                  (fn  [n]                      (s/validate  Animals  n))))     ;;  initial  call  to  get  animals  from  server   (go  (let  [response                      (<!  (http/get  "/animals"))                      data  (:body  response)]              (reset!  animals-­‐state  (set  data))))     RAtom with set containing animal hash-maps (...      {:id  2,      :type  :animal,      :name  "Yellow-­‐backed  duiker",      :species  "Cephalophus  silvicultor"}    {:id  1,      :type  :animal,      :name  "Painted-­‐snipe",      :species  "Rostratulidae"}  
  • 38.
    Render  all  animals  from  state   (defn  animals  []      [:div        [:table.table.table-­‐striped          [:thead            [:tr              [:th  "Name"]  [:th  "Species"]  [:th  ""]  [:th  ""]]]          [:tbody            (map  (fn  [a]                          ^{:key  (str  "animal-­‐row-­‐"  (:id  a))}                          [animal-­‐row  a])                      (sort-­‐by  :name  @animals-­‐state))            [animal-­‐form]]]])    
  • 39.
    animal-­‐row  component   {:editing?  false,  :name  "Yellow-­‐backed  duiker”,  :species  "Cephalophus  silvicultor"}   {:editing?  true,  :name  "Yellow-­‐backed  pony”,  :species  "Cephalophus  silvicultor"}  
  • 40.
    (defn  animal-­‐row  [a]      (let  [row-­‐state  (atom  {:editing?  false                                                    :name          (:name  a)                                                    :species    (:species  a)})                  current-­‐animal  (fn  []                                                    (assoc  a                                                        :name  (:name  @row-­‐state)                                                        :species  (:species  @row-­‐state)))]          (fn  []              [:tr                [:td  [editable-­‐input  row-­‐state  :name]]                [:td  [editable-­‐input  row-­‐state  :species]]                [:td  [:button.btn.btn-­‐primary.pull-­‐right                            {:disabled  (not  (input-­‐valid?  row-­‐state))                              :on-­‐click  (fn  []                                                      (when  (:editing?  @row-­‐state)                                                          (update-­‐animal!  (current-­‐animal)))                                                      (swap!  row-­‐state  update-­‐in  [:editing?]  not))}                            (if  (:editing?  @row-­‐state)  "Save"  "Edit")]]                [:td  [:button.btn.pull-­‐right.btn-­‐danger                            {:on-­‐click  #(remove-­‐animal!  (current-­‐animal))}                            "u00D7"]]])))    
  • 41.
    (defn  editable-­‐input  [atom  key]      (if  (:editing?  @atom)          [:input  {:type          "text"                            :value        (get  @atom  key)                            :on-­‐change  (fn  [e]  (swap!  atom                                                                                assoc  key                                                                                (..  e  -­‐target  -­‐value)))}]          [:p  (get  @atom  key)]))     {:editing?  false,  :name  "Yellow-­‐backed  duiker",  :species  "Cephalophus  silvicultor"}   {:editing?  true,  :name  "Yellow-­‐backed  pony",  :species  "Cephalophus  silvicultor"}  
  • 42.
    (defn  animal-­‐row  [a]      (let  [row-­‐state  (atom  {:editing?  false                                                    :name          (:name  a)                                                    :species    (:species  a)})                  current-­‐animal  (fn  []                                                    (assoc  a                                                        :name  (:name  @row-­‐state)                                                        :species  (:species  @row-­‐state)))]          (fn  []              [:tr                [:td  [editable-­‐input  row-­‐state  :name]]                [:td  [editable-­‐input  row-­‐state  :species]]                [:td  [:button.btn.btn-­‐primary.pull-­‐right                            {:disabled  (not  (input-­‐valid?  row-­‐state))                              :on-­‐click  (fn  []                                                      (when  (:editing?  @row-­‐state)                                                          (update-­‐animal!  (current-­‐animal)))                                                      (swap!  row-­‐state  update-­‐in  [:editing?]  not))}                            (if  (:editing?  @row-­‐state)  "Save"  "Edit")]]                [:td  [:button.btn.pull-­‐right.btn-­‐danger                            {:on-­‐click  #(remove-­‐animal!  (current-­‐animal))}                            "u00D7"]]])))    
  • 43.
    (defn  input-­‐valid?  [atom]      (and  (seq  (-­‐>  @atom  :name))                (seq  (-­‐>  @atom  :species))))    
  • 44.
    (defn  animal-­‐row  [a]      (let  [row-­‐state  (atom  {:editing?  false                                                    :name          (:name  a)                                                    :species    (:species  a)})                  current-­‐animal  (fn  []                                                    (assoc  a                                                        :name  (:name  @row-­‐state)                                                        :species  (:species  @row-­‐state)))]          (fn  []              [:tr                [:td  [editable-­‐input  row-­‐state  :name]]                [:td  [editable-­‐input  row-­‐state  :species]]                [:td  [:button.btn.btn-­‐primary.pull-­‐right                            {:disabled  (not  (input-­‐valid?  row-­‐state))                              :on-­‐click  (fn  []                                                      (when  (:editing?  @row-­‐state)                                                          (update-­‐animal!  (current-­‐animal)))                                                      (swap!  row-­‐state  update-­‐in  [:editing?]  not))}                            (if  (:editing?  @row-­‐state)  "Save"  "Edit")]]                [:td  [:button.btn.pull-­‐right.btn-­‐danger                            {:on-­‐click  #(remove-­‐animal!  (current-­‐animal))}                            "u00D7"]]])))    
  • 45.
    (defn  update-­‐animal!  [a]      (go  (let  [response                          (<!  (http/put  (str  "/animals/"  (:id  a))                                                      {:edn-­‐params  a}))                          updated-­‐animal  (:body  response)]                  (swap!  animals-­‐state                                (fn  [old-­‐state]                                    (conj                                        (remove-­‐by-­‐id  old-­‐state  (:id  a))                                        updated-­‐animal))))))    
  • 46.
    (defn  animal-­‐row  [a]      (let  [row-­‐state  (atom  {:editing?  false                                                    :name          (:name  a)                                                    :species    (:species  a)})                  current-­‐animal  (fn  []                                                    (assoc  a                                                        :name  (:name  @row-­‐state)                                                        :species  (:species  @row-­‐state)))]          (fn  []              [:tr                [:td  [editable-­‐input  row-­‐state  :name]]                [:td  [editable-­‐input  row-­‐state  :species]]                [:td  [:button.btn.btn-­‐primary.pull-­‐right                            {:disabled  (not  (input-­‐valid?  row-­‐state))                              :on-­‐click  (fn  []                                                      (when  (:editing?  @row-­‐state)                                                          (update-­‐animal!  (current-­‐animal)))                                                      (swap!  row-­‐state  update-­‐in  [:editing?]  not))}                            (if  (:editing?  @row-­‐state)  "Save"  "Edit")]]                [:td  [:button.btn.pull-­‐right.btn-­‐danger                            {:on-­‐click  #(remove-­‐animal!  (current-­‐animal))}                            "u00D7"]]])))    
  • 47.
    (defn  remove-­‐animal!  [a]      (go  (let  [response                          (<!  (http/delete  (str  "/animals/"                                                                      (:id  a))))]                  (if  (=  200  (:status  response))                      (swap!  animals-­‐state  remove-­‐by-­‐id  (:id  a))))))       if server says: "OK!", remove animal from CRUD table
  • 48.
    Exercises   -­‐  Sort  table  by  clicking  on  name  or  species   -­‐  OpTmisTc  updates   Code  and  slides  at:   h[ps://github.com/borkdude/domcode-­‐cljs-­‐react    
  • 49.
    •  Install  JDK  7+   •  Install  leiningen  (build  tool)   •  git  clone  https://github.com/borkdude/domcode-­‐cljs-­‐react.git     •  cd  domcode-­‐cljs-­‐react/code/animals-­‐crud   •  See  README.md  for  further  instrucTons   Probably  Cursive  IDE  (IntelliJ)  is  most  beginner  friendly             How  to  run  at  home?  
  • 50.
    Leiningen   ●  Used  by  98%  of  Clojure  users   ●  Clojure's  Maven   ●  Managing  dependencies   ●  Running  a  REPL   ●  Packaging  and  deploying   ●  Plugins:   ○  lein  cljsbuild  –  building  ClojureScript   ○  lein  figwheel  –  live  code  reloading  in   browser  
  • 51.
    Debugging   Source  maps  let  you  debug  ClojureScript  directly  from  the  browser    
  • 52.
    Get  started  with  Clojure(Script)   •  Read  a  Clojure(Script)  book   •  Do  the  4clojure  exercises   •  Start  hacking  on  your  own  project   •  Pick  an  online  Clojure  course     •  Join  the  AMSCLJ  meetup   •  Join  the  Slack  community   Thanks!