2014-12-10 11:02:47 +00:00
|
|
|
|
(ns reagentdemo.news.news050
|
2015-07-31 07:58:07 +00:00
|
|
|
|
(:require [reagent.core :as r]
|
2014-12-10 11:02:47 +00:00
|
|
|
|
[reagent.debug :refer-macros [dbg println]]
|
2015-02-09 20:02:31 +00:00
|
|
|
|
[reagentdemo.syntax :as s]
|
2015-09-08 18:18:27 +00:00
|
|
|
|
[sitetools.core :as tools :refer [link]]
|
2015-02-10 13:18:56 +00:00
|
|
|
|
[reagentdemo.common :as common :refer [demo-component]]))
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
2015-09-08 18:18:27 +00:00
|
|
|
|
(def url "/news/news050.html")
|
2015-03-11 16:50:33 +00:00
|
|
|
|
(def title "News in 0.5.0")
|
|
|
|
|
|
2015-10-13 11:03:07 +00:00
|
|
|
|
(def new-in-alpha nil)
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
|
|
|
|
(def ns-src (s/syntaxed "(ns example
|
2015-07-31 07:58:07 +00:00
|
|
|
|
(:require [reagent.core :as r]))"))
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
(def cel-link "http://facebook.github.io/react/docs/top-level-api.html#react.createelement")
|
|
|
|
|
|
|
|
|
|
(def figwheel-link "https://github.com/bhauman/lein-figwheel")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-07-31 07:58:07 +00:00
|
|
|
|
(defonce person (r/atom {:name
|
|
|
|
|
{:first-name "John" :last-name "Smith"}}))
|
2015-03-11 16:50:33 +00:00
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
(defn input [prompt val]
|
|
|
|
|
[:div
|
|
|
|
|
prompt
|
|
|
|
|
[:input {:value @val
|
|
|
|
|
:on-change #(reset! val (.-target.value %))}]])
|
|
|
|
|
|
|
|
|
|
(defn name-edit [n]
|
|
|
|
|
(let [{:keys [first-name last-name]} @n]
|
|
|
|
|
[:div
|
|
|
|
|
[:p "I'm editing " first-name " " last-name "."]
|
2015-10-13 11:03:07 +00:00
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
[input "First name: " (r/wrap first-name
|
|
|
|
|
swap! n assoc :first-name)]
|
|
|
|
|
[input "Last name: " (r/wrap last-name
|
|
|
|
|
swap! n assoc :last-name)]]))
|
|
|
|
|
|
|
|
|
|
(defn parent []
|
|
|
|
|
[:div
|
|
|
|
|
[:p "Current state: " (pr-str @person)]
|
|
|
|
|
[name-edit (r/wrap (:name @person)
|
|
|
|
|
swap! person assoc :name)]])
|
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
(defn cursor-name-edit [n]
|
|
|
|
|
(let [{:keys [first-name last-name]} @n]
|
|
|
|
|
[:div
|
|
|
|
|
[:p "I'm editing " first-name " " last-name "."]
|
|
|
|
|
|
|
|
|
|
[input "First name: " (r/cursor n [:first-name])]
|
|
|
|
|
[input "Last name: " (r/cursor n [:last-name])]]))
|
|
|
|
|
|
|
|
|
|
(defn cursor-parent []
|
|
|
|
|
[:div
|
|
|
|
|
[:p "Current state: " (pr-str @person)]
|
|
|
|
|
[cursor-name-edit (r/cursor person [:name])]])
|
|
|
|
|
|
|
|
|
|
(defn person-get-set
|
|
|
|
|
([k] (get-in @person k))
|
|
|
|
|
([k v] (swap! person assoc-in k v)))
|
|
|
|
|
|
|
|
|
|
(defn get-set-parent []
|
|
|
|
|
[:div
|
|
|
|
|
[:p "Current state: " (pr-str @person)]
|
|
|
|
|
[cursor-name-edit (r/cursor person-get-set [:name])]])
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
|
|
|
|
(defn integration []
|
|
|
|
|
[:div
|
|
|
|
|
[:div.foo "Hello " [:strong "world"]]
|
|
|
|
|
|
|
|
|
|
(r/create-element "div"
|
|
|
|
|
#js{:className "foo"}
|
|
|
|
|
"Hello "
|
|
|
|
|
(r/create-element "strong"
|
|
|
|
|
#js{}
|
|
|
|
|
"world"))
|
|
|
|
|
|
|
|
|
|
(r/create-element "div"
|
|
|
|
|
#js{:className "foo"}
|
|
|
|
|
"Hello "
|
|
|
|
|
(r/as-element [:strong "world"]))
|
|
|
|
|
|
|
|
|
|
[:div.foo "Hello " (r/create-element "strong"
|
|
|
|
|
#js{}
|
|
|
|
|
"world")]])
|
|
|
|
|
|
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
(def div-adapter (r/adapt-react-class "div"))
|
|
|
|
|
|
|
|
|
|
(defn adapted []
|
|
|
|
|
[div-adapter {:class "foo"}
|
|
|
|
|
"Hello " [:strong "world"]])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(defn exported [props]
|
|
|
|
|
[:div "Hi, " (:name props)])
|
|
|
|
|
|
|
|
|
|
(def react-comp (r/reactify-component exported))
|
|
|
|
|
|
|
|
|
|
(defn could-be-jsx []
|
|
|
|
|
(r/create-element react-comp #js{:name "world"}))
|
|
|
|
|
|
2015-09-08 18:18:27 +00:00
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
(defn main [{:keys [summary]}]
|
|
|
|
|
[:div.reagent-demo
|
2015-09-08 18:18:27 +00:00
|
|
|
|
[:h1 [link {:href url} title]]
|
2017-11-28 17:16:26 +00:00
|
|
|
|
[:span "2014-12-10"]
|
2014-12-10 11:02:47 +00:00
|
|
|
|
[:div.demo-text
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:p "Reagent 0.5.0 has automatic importing of React.js, two kinds
|
|
|
|
|
of cursors, better integration of native React components, better
|
|
|
|
|
performance, easier integration with e.g Figwheel, and more."]
|
2015-10-22 11:23:49 +00:00
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
(if summary
|
2015-09-08 18:18:27 +00:00
|
|
|
|
[link {:href url
|
2014-12-10 11:02:47 +00:00
|
|
|
|
:class 'news-read-more} "Read more"]
|
|
|
|
|
[:div.demo-text
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:h2 "A new way of importing React"]
|
|
|
|
|
|
|
|
|
|
[:p new-in-alpha "Reagent now takes advantage of
|
|
|
|
|
ClojureScript’s new way of packaging JavaScript dependencies.
|
|
|
|
|
That means that you no longer have to include React in your
|
|
|
|
|
HTML, nor should you use " [:code ":preamble"] ". Instead,
|
|
|
|
|
Reagent depends on the " [:code "cljsjs/react"] " library."]
|
|
|
|
|
|
|
|
|
|
[:p "If you want to use another version of React, you can do
|
|
|
|
|
that in two ways. In both cases you’ll have to
|
|
|
|
|
exclude " [:code "cljsjs/react"] " by using
|
|
|
|
|
e.g " [:code "[reagent \"0.5.0-alpha3\" :exclusions [cljsjs/react]]"]
|
|
|
|
|
" in the " [:code ":dependencies"] " section of your "
|
|
|
|
|
[:code "project.clj"] "."]
|
|
|
|
|
|
|
|
|
|
[:p "You can then add e.g " [:code "[cljsjs/react-with-addons
|
|
|
|
|
\"0.12.2-4\"]"] " as a dependency. Or you can add a file
|
|
|
|
|
named " [:code "cljsjs/react.cljs"] ", containing
|
|
|
|
|
just " [:code "(ns cljsjs.react)"] ", to your project – and then
|
|
|
|
|
import React in some other way."]
|
|
|
|
|
|
|
|
|
|
[:p "Reagent now requires ClojureScript 0.0-2816 or later."]
|
|
|
|
|
|
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
[:h2 "Splitting atoms"]
|
|
|
|
|
|
|
|
|
|
[:p "Reagent now has a simple way to make reusable components
|
|
|
|
|
that edit part of a parents state: "]
|
|
|
|
|
|
|
|
|
|
[:p " The new " [:code "wrap"] "
|
|
|
|
|
function combines two parts – a simple value, and a callback
|
|
|
|
|
for when that value should change – into one value, that
|
|
|
|
|
happens to look as an atom. "]
|
|
|
|
|
|
|
|
|
|
[:p "The first argument to " [:code "wrap"] " should be the
|
|
|
|
|
value that will be returned by " [:code "@the-result"] "."]
|
|
|
|
|
|
|
|
|
|
[:p "The second argument should be a function, that will be
|
|
|
|
|
passed the new value whenever the result is changed (with
|
|
|
|
|
optional extra arguments to " [:code "wrap"] " prepended, as
|
|
|
|
|
with " [:code "partial"] ")."]
|
|
|
|
|
|
|
|
|
|
[:p "Usage can look something like this:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp parent
|
|
|
|
|
:src [:pre ns-src
|
2015-03-11 16:50:33 +00:00
|
|
|
|
(s/src-of [:person :input :name-edit
|
|
|
|
|
:parent])]}]
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
|
|
|
|
[:p "Here, the " [:code "parent"] " component controls the
|
|
|
|
|
global state, and delegates editing the name
|
|
|
|
|
to " [:code "name-edit"] ". " [:code "name-edit"] " in turn
|
|
|
|
|
delegates the actual input of first and last names
|
|
|
|
|
to " [:code "input"] "."]
|
|
|
|
|
|
|
|
|
|
[:p [:b "Note: "] "The result from " [:code "wrap"] " is just a
|
|
|
|
|
simple and light-weight value, that happens to look like an
|
|
|
|
|
atom – it doesn’t by itself trigger any re-renderings
|
|
|
|
|
like " [:code "reagent.core/atom"] " does. That means that it
|
|
|
|
|
is probably only useful to pass from one component to another,
|
|
|
|
|
and that the callback function in the end must cause a ”real”
|
|
|
|
|
atom to change."]
|
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:h2 "Cursors"]
|
|
|
|
|
|
|
|
|
|
[:p new-in-alpha "Reagent has another way of isolating part of
|
|
|
|
|
a data structure in an atom: " [:code "reagent.core/cursor"] ".
|
|
|
|
|
Using the same state as in the previous example, usage now looks
|
|
|
|
|
like this:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp cursor-parent
|
|
|
|
|
:src (s/src-of [:cursor-name-edit
|
|
|
|
|
:cursor-parent])}]
|
|
|
|
|
|
|
|
|
|
[:p new-in-alpha "Cursors can now also be generalized to use
|
|
|
|
|
any transformation of data from and to a source atom (or many
|
|
|
|
|
atoms, for that matter). To use that, you pass a function
|
|
|
|
|
to " [:code "cursor"] " instead of an atom, as in this
|
|
|
|
|
example:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp cursor-parent
|
|
|
|
|
:src (s/src-of [:person-get-set
|
|
|
|
|
:get-set-parent])}]
|
|
|
|
|
|
|
|
|
|
[:p "The function passed to " [:code "cursor"] " will be called
|
|
|
|
|
with one argument to get data (it is passed the key, i.e the
|
|
|
|
|
second argument to " [:code "cursor"] "), and two arguments
|
|
|
|
|
when the cursor is changed (then it is passed the key and the
|
|
|
|
|
new value)."]
|
|
|
|
|
|
|
|
|
|
[:p "The getter function can reference one or many Reagent
|
|
|
|
|
atoms (or other cursors). If the cursor is used in a component
|
|
|
|
|
the getter function will re-run to change the value of the
|
|
|
|
|
cursor just like a Reagent component does."]
|
|
|
|
|
|
2015-10-13 11:03:07 +00:00
|
|
|
|
[:h2 "Values and references"]
|
2015-03-11 16:50:33 +00:00
|
|
|
|
|
|
|
|
|
[:p "So what’s the difference between wraps and cursors? Why
|
|
|
|
|
have both?"]
|
|
|
|
|
|
|
|
|
|
[:p "A " [:code "wrap"] " is just a value that happens to look
|
|
|
|
|
like an " [:code "atom"] ". It doesn’t change unless you tell
|
|
|
|
|
it to. It is a very lightweight combination of value and a
|
|
|
|
|
callback to back-propagate changes to the value. It relies only
|
|
|
|
|
on Reagent’s equality test
|
|
|
|
|
in " [:code ":should-component-update"] " to avoid unnecessary
|
|
|
|
|
re-rendering."]
|
|
|
|
|
|
|
|
|
|
[:p "A " [:code "cursor"] ", on the other hand, will always be
|
|
|
|
|
up-to-date with the value of the source atom. In other words,
|
|
|
|
|
it acts a reference to part of the value of the source.
|
|
|
|
|
Components that " [:code "deref"] " cursors are re-rendered
|
|
|
|
|
automatically, in exactly the same way as if
|
|
|
|
|
they " [:code "deref"] " a normal Reagent atom (unnecessary
|
|
|
|
|
re-rendering is avoided by checking if the cursor's value has
|
|
|
|
|
changed using " [:code "identical?"] ")."]
|
2015-10-22 11:23:49 +00:00
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
|
|
|
|
[:h2 "Faster rendering"]
|
|
|
|
|
|
|
|
|
|
[:p "Reagent used to wrap all ”native” React components in an
|
|
|
|
|
extra Reagent component, in order to keep track of how deep in
|
|
|
|
|
the component tree each component was (to make sure that
|
|
|
|
|
un-necessary re-renderings were avoided)."]
|
|
|
|
|
|
|
|
|
|
[:p "Now, this extra wrapper-component isn’t needed anymore,
|
|
|
|
|
which means quite a bit faster generation of native React
|
|
|
|
|
elements. This will be noticeable if you generate html strings,
|
|
|
|
|
or if you animate a large number of components."]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[:h2 "Simple React integration"]
|
|
|
|
|
|
|
|
|
|
[:p "Since Reagent doesn't need those wrappers anymore it is
|
|
|
|
|
also now easier to mix native React components with Reagent
|
|
|
|
|
ones. There’s a new convenience
|
|
|
|
|
function, " [:code "reagent.core/create-element"] ", that
|
|
|
|
|
simply calls " [:a {:href
|
|
|
|
|
cel-link} [:code "React.createElement"]] ". This,
|
|
|
|
|
unsurprisingly, creates React elements, either from the result
|
|
|
|
|
of " [:code "React.createClass"] " or html tag names."]
|
|
|
|
|
|
|
|
|
|
[:p [:code "reagent.core/as-element"] " turns Reagent’s hiccup
|
|
|
|
|
forms into React elements, that can be passed to ordinary React
|
|
|
|
|
components. The combination of " [:code "create-element"] "
|
|
|
|
|
and " [:code "as-element"] " allows mixing and matching of
|
|
|
|
|
Reagent and React components."]
|
|
|
|
|
|
|
|
|
|
[:p "For an example, here are four different ways to achieve
|
|
|
|
|
the same thing:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp integration
|
|
|
|
|
:src (s/src-of [:integration])}]
|
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:p new-in-alpha "If you don't need/want this kind of low-level
|
|
|
|
|
control over interaction with javascript React, you can also
|
|
|
|
|
use the new function " [:code "adapt-react-class"] ", that will
|
|
|
|
|
take any React class, and turn it into something that can be
|
|
|
|
|
called from Reagent directly. The example from above would then
|
|
|
|
|
become:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp adapted
|
|
|
|
|
:src (s/src-of [:div-adapter :adapted])}]
|
|
|
|
|
|
|
|
|
|
[:p new-in-alpha "You can also do the opposite: call Reagent
|
|
|
|
|
components from JavaScript React (for example from JSX). For
|
|
|
|
|
this purpose, you'd use another adapter
|
|
|
|
|
– " [:code "reactify-component"] " – like this:"]
|
|
|
|
|
|
|
|
|
|
[demo-component {:comp could-be-jsx
|
|
|
|
|
:src (s/src-of [:exported :react-comp
|
|
|
|
|
:could-be-jsx])}]
|
|
|
|
|
|
|
|
|
|
[:p "The " [:code "exported"] " component will be called with a
|
|
|
|
|
single argument: the React " [:code "props"] ", converted to a
|
|
|
|
|
ClojureScript " [:code "map"] "."]
|
|
|
|
|
|
2014-12-10 11:02:47 +00:00
|
|
|
|
|
|
|
|
|
[:h2 "More equality"]
|
|
|
|
|
|
|
|
|
|
[:p "Reagent used to have a rather complicated way of
|
|
|
|
|
determining when a component should be re-rendered in response
|
|
|
|
|
to changing arguments. Now the rule is much simpler: a
|
|
|
|
|
component will be re-rendered if the old and new arguments are
|
|
|
|
|
not equal (i.e. they are compared with a
|
|
|
|
|
simple " [:code "="] ")."]
|
|
|
|
|
|
|
|
|
|
[:p [:strong "Note: "] "This is a breaking change! It means
|
|
|
|
|
that you can no longer pass infinite " [:code "seq"] "s to a
|
|
|
|
|
component."]
|
|
|
|
|
|
|
|
|
|
[:h2 "React 0.12"]
|
|
|
|
|
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:p "Reagent now comes with, and requires, React 0.12.2. To
|
2014-12-10 11:02:47 +00:00
|
|
|
|
mirror the changes in API in React, some Reagent functions have
|
|
|
|
|
gotten new names: "]
|
|
|
|
|
|
|
|
|
|
[:ul
|
|
|
|
|
[:li [:code "render-component"] " is now " [:code "render"]]
|
2015-03-11 16:50:33 +00:00
|
|
|
|
[:li [:code "render-component-to-string"] " is
|
|
|
|
|
now " [:code "render-to-string"]]
|
2014-12-10 11:02:47 +00:00
|
|
|
|
[:li [:code "as-component"] " is now " [:code "as-element"]]]
|
|
|
|
|
|
|
|
|
|
[:p "The old names still work, though."]
|
|
|
|
|
|
|
|
|
|
[:p "There is also a new
|
|
|
|
|
function, " [:code "render-to-static-markup"] ", that works
|
|
|
|
|
just like render-to-string, except that it doesn’t add
|
|
|
|
|
React-specific attributes."]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[:h2 "Easier live-programming"]
|
|
|
|
|
|
|
|
|
|
[:p "It is now easier than before to integrate Reagent with
|
|
|
|
|
e.g. the rather excellent " [:a {:href
|
|
|
|
|
figwheel-link} "figwheel"] ", since " [:code "render"] " now
|
|
|
|
|
will cause the entire component tree to update (by-passing the
|
2015-03-11 16:50:33 +00:00
|
|
|
|
equality checks)."]
|
|
|
|
|
|
|
|
|
|
[:p new-in-alpha "All the examples in the Reagent repo now uses
|
|
|
|
|
figwheel."]])]])
|
2015-09-08 18:18:27 +00:00
|
|
|
|
|
|
|
|
|
(tools/register-page url [#'main] title)
|