{"id":642,"date":"2014-08-25T22:38:53","date_gmt":"2014-08-26T05:38:53","guid":{"rendered":"http:\/\/pchristensen.com\/blog\/?p=642"},"modified":"2014-08-26T09:45:21","modified_gmt":"2014-08-26T16:45:21","slug":"sfcljs-august-meetup-om","status":"publish","type":"post","link":"http:\/\/pchristensen.com\/blog\/articles\/sfcljs-august-meetup-om\/","title":{"rendered":"#sfcljs August Meetup &#8211; Om vs Reagent vs Quiescent, Converting to Om"},"content":{"rendered":"<p>[UPDATE: Added links to slides for some of the talks]<\/p>\n<p>ClojureScript is my new toy, so I was glad to see the San Francisco <a href=\"http:\/\/www.meetup.com\/SF-Clojurescript-Meetup\/\">ClojureScript Meetup<\/a> plan a new event, and all of the talks were about Om, which made it even better. It was a great event at Etsy&#8217;s SF office. Thanks to Melissa and Sean Grove for organizing it!<\/p>\n<p>Here are my notes:<\/p>\n<ul>\n<li><a href=\"http:\/\/www.meetup.com\/SF-Clojurescript-Meetup\/events\/201007552\/\">http:\/\/www.meetup.com\/SF-Clojurescript-Meetup\/events\/201007552\/<\/a><\/li>\n<li>Official Meetup Hashtag: #sfcljs &#8211; <a href=\"https:\/\/twitter.com\/search?q=#sfcljs\">https:\/\/twitter.com\/search?q=%23sfcljs<\/a><\/li>\n<li>Etsy (host) is hiring: <a href=\"https:\/\/www.etsy.com\/careers\/\">https:\/\/www.etsy.com\/careers\/<\/a><\/li>\n<li>Diogo\n<ul>\n<li>Slides:\u00a0<a href=\"http:\/\/htmlpreview.github.io\/?https:\/\/github.com\/diogo149\/om-no\/blob\/master\/notes.html\">http:\/\/htmlpreview.github.io\/?https:\/\/github.com\/diogo149\/om-no\/blob\/master\/notes.html<\/a><\/li>\n<li>Project: Social network with data mining, in stealth\n<ul>\n<li>switched from Om to Reagent<\/li>\n<li>hit a wall after 5k+ LOC, months<\/li>\n<\/ul>\n<\/li>\n<li>What was good about Om\n<ul>\n<li>Global undo &#8211; really easy, but didn&#8217;t apply to the app<\/li>\n<li>One atom to contain global app state<\/li>\n<\/ul>\n<\/li>\n<li>The Bad\n<ul>\n<li>reify and build\/build-all was error prone &#8211; you have to keep straight which fns return reified functions and which are normal<\/li>\n<li>API is verbose\n<ul>\n<li>he built syntactic sugar for it, but that shows weakness with API<\/li>\n<\/ul>\n<\/li>\n<li>Too slow &#8211; much slower than Angular JS<\/li>\n<li>No anonymous fns as components\n<ul>\n<li>didn&#8217;t throw error messages, just re-mounted and caused site to flicker<\/li>\n<\/ul>\n<\/li>\n<li>Can&#8217;t compose components, because components track their parent<\/li>\n<li>Cursors &#8211; pointers into app state\n<ul>\n<li>async transact! can change the value or order of the app state, making the cursor point to the wrong data<\/li>\n<\/ul>\n<\/li>\n<li>Local state mixed with global state\n<ul>\n<li>you can&#8217;t wholesale replace new app state, b\/c you don&#8217;t know where it&#8217;s mixed with local state that you would lose<\/li>\n<\/ul>\n<\/li>\n<li>cursors behave like maps\n<ul>\n<li>you can pass maps like cursors, except for when you want to transact!<\/li>\n<\/ul>\n<\/li>\n<li>Need to Know principle\n<ul>\n<li>In React, components work best when they know as little as possible, so they don&#8217;t re-render for unnecessary changes<\/li>\n<li>In Om, app state is a tree, so you need knowledge of lowest common ancestor<\/li>\n<li>same data in multiple places<\/li>\n<\/ul>\n<\/li>\n<li>hard to be both consistent and performant<\/li>\n<\/ul>\n<\/li>\n<li>Reagent\n<ul>\n<li>principle of least surprise<\/li>\n<li>more Clojure-y &#8211; lazy components, normal functions,<\/li>\n<li>data model works better with React.js<\/li>\n<li>easier to work with data as a direct acyclic graph<\/li>\n<li>We were able to teach a new employee the codebase without knowledge of app or Reagent &#8211; testament of a good API<\/li>\n<li>Simpler for small apps, more scaleable for large apps<\/li>\n<\/ul>\n<\/li>\n<li>Reagent &#8211; <a href=\"https:\/\/github.com\/holmsand\/reagent\">https:\/\/github.com\/holmsand\/reagent<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Anton &#8211; <a href=\"http:\/\/textik.com\/\">http:\/\/textik.com\/<\/a>\n<ul>\n<li>Slides:\u00a0<a href=\"http:\/\/www.slideshare.net\/AntonAstashov\/textik\">http:\/\/www.slideshare.net\/AntonAstashov\/textik<\/a><\/li>\n<li>ASCII diagram generator<\/li>\n<li>Pet project, so he needed something he could build in an hour a day<\/li>\n<li>Normal design of front-end web apps\n<ul>\n<li>observe changes in view models by any other observer models, sometimes cascading<\/li>\n<li>Works well, but hard to figure out what happened in exception<\/li>\n<li>Many inputs, many code paths<\/li>\n<\/ul>\n<\/li>\n<li>Prefer one-way flow of events \u2192 data \u2192 rendering \u2192 events\n<ul>\n<li>One global code path<\/li>\n<li>Full state of the app when exceptions happen<\/li>\n<\/ul>\n<\/li>\n<li>Like React for fast view layer, use Quiescent for cljs\n<ul>\n<li>lightweight, ~200 LOC<\/li>\n<li>Simple<\/li>\n<li>Opinionless<\/li>\n<\/ul>\n<\/li>\n<li>Cons\n<ul>\n<li>slowly developing &#8211; few recent commits, pending pull requests<\/li>\n<li>you have to maintain your own fork for e.g. animations<\/li>\n<li>Has bugs that are fixed in Om<\/li>\n<\/ul>\n<\/li>\n<li>Lifecycle\n<ul>\n<li>Init &#8211; call Render<\/li>\n<li>View &#8211; render, register event handlers that send payload to core.async channel\n<ul>\n<li>core.async used b\/c you can&#8217;t have circular dependencies on namespaces, since view would require dispatch and vice versa<\/li>\n<\/ul>\n<\/li>\n<li>Dispatch &#8211; listen to channel, call mutators, call render<\/li>\n<li>Mutators &#8211; change data (only layer where data is changed)<\/li>\n<li>Data \u2192 return to Dispatch<\/li>\n<\/ul>\n<\/li>\n<li>Code &#8211; <a href=\"https:\/\/github.com\/astashov\/tixi\">https:\/\/github.com\/astashov\/tixi<\/a><\/li>\n<li>Minimal app using Quiescent, this architecture &#8211; <a href=\"https:\/\/github.com\/astashov\/mintik\">https:\/\/github.com\/astashov\/mintik<\/a><\/li>\n<li>Quiescent &#8211; <a href=\"https:\/\/github.com\/levand\/quiescent\">https:\/\/github.com\/levand\/quiescent<\/a><\/li>\n<\/ul>\n<\/li>\n<li>Danny from <a href=\"https:\/\/circleci.com\/\">CircleCI<\/a> &#8211; Om From a Designer&#8217;s Point of View\n<ul>\n<li>Does not like writing Om, but likes how everything is in one language, so it&#8217;s easier to reason about (vs HAML, CoffeeScript, CSS, etc)<\/li>\n<li>Can be harder to find where the data is coming from<\/li>\n<li>Only sort of gets the lifecycle, design of components\n<ul>\n<li>Learning is coming faster than expected<\/li>\n<\/ul>\n<\/li>\n<li>Workflow + tools are the same except for a ~10sec delay when making changes\n<ul>\n<li>Using production data, and has to use the whitespace compiler to produce a single Javascript file<\/li>\n<li>Sublime, Chrome Dev tools, etc<\/li>\n<\/ul>\n<\/li>\n<li>This is not a natural tool for a designer &#8211; most would need someone to help with the more programmery\/Clojure-y stuff<\/li>\n<li>Syntax for Hiccup etc isn&#8217;t a big change from HTML\/CSS<\/li>\n<li>Could use a more applicable cheat sheet for designers<\/li>\n<li>Uses CSS or SVG animations<\/li>\n<li>Surprised by React making assumptions\/transformations on markup &#8211; e.g. moving an anchor to be a sibling of inline tag, from inside of it<\/li>\n<li>Design process would be easier if he ran a local dev environment, but that&#8217;s a big new dependency that requires maintenance<\/li>\n<\/ul>\n<\/li>\n<li>Daniel from <a href=\"https:\/\/circleci.com\/\">CircleCI<\/a> &#8211; Converting a Site from Knockout to Om\n<ul>\n<li>Can now use CircleCI for open source projects &#8211; free container, public build pages<\/li>\n<li>Open sourced front-end code: <a href=\"https:\/\/github.com\/circleci\/frontend\">https:\/\/github.com\/circleci\/frontend<\/a>\n<ul>\n<li>You connect to your own CircleCI backend data, so you can push changes\/fixes to be merged into the main, public repo, while protecting your data<\/li>\n<li>Still working out details of the open source workflow<\/li>\n<\/ul>\n<\/li>\n<li>(this was more showing code than presentation)<\/li>\n<li>Old site used Knockout.js, 2-way data binding\n<ul>\n<li>data binding in JS, HAML-coffee templates in CoffeeScript, etc, so there were language mismatches, string interpolation<\/li>\n<\/ul>\n<\/li>\n<li>Converter from HAML-coffee to Hiccup &#8211; not perfect, but close enough\n<ul>\n<li>Difference between whitespace automatically included vs required<\/li>\n<\/ul>\n<\/li>\n<li>Event handlers put messages into core.async channels<\/li>\n<li>Took about 2.5 months full time development to convert<\/li>\n<li>How it works\n<ul>\n<li>All state is serializable in a single Atom<\/li>\n<li>Build messages &#8211; something we know about the build that we want to tell you (~100 messages)\n<ul>\n<li>Gave a representative sample of serialized state to designer<\/li>\n<\/ul>\n<\/li>\n<li>In theory you can store and replay messages to rebuild state<\/li>\n<li>core.async &#8211; e.g. Stripe checkout &#8211; complete\/cancel puts message into channel, and the page blocks on that<\/li>\n<\/ul>\n<\/li>\n<li>Problems with Om\n<ul>\n<li>haven&#8217;t figured out testing, still just using webdriver tests<\/li>\n<li>Rendering too much to the virtual DOM &#8211; parents have to know about child data, even if they don&#8217;t need it\n<ul>\n<li>hasn&#8217;t been too big a problem yet<\/li>\n<\/ul>\n<\/li>\n<li>Slow enough that you can drop characters on input, because of how much rendering goes on when everything is in global app state\n<ul>\n<li>More of a problem in tests<\/li>\n<li>Needed to pull input data out of app state and into local component state<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>[UPDATE: Added links to slides for some of the talks] ClojureScript is my new toy, so I was glad to see the San Francisco ClojureScript Meetup plan a new event, and all of the talks were about Om, which made it even better. It was a great event at Etsy&#8217;s SF office. Thanks to Melissa [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[14,10],"tags":[],"class_list":{"0":"post-642","1":"post","2":"type-post","3":"status-publish","4":"format-standard","6":"category-clojure","7":"category-programming","8":"entry"},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pazgP-am","_links":{"self":[{"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/posts\/642","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/comments?post=642"}],"version-history":[{"count":0,"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/posts\/642\/revisions"}],"wp:attachment":[{"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/media?parent=642"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/categories?post=642"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/pchristensen.com\/blog\/wp-json\/wp\/v2\/tags?post=642"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}