Mike, thanks for sharing! This helped clarify some of my thoughts and 
introduced me to a couple of possibilities I hadn't considered. I'd enjoy 
seeing a demo app if you do decide to publish one.

On Wednesday, October 8, 2014 3:05:06 PM UTC-4, Mike Haney wrote:
> I've taken a different approach which I think has really worked well, so I 
> thought I would share it.
> 
> The inspiration for my approach came from this article 
> http://tonsky.me/blog/datascript-chat, in which he uses Datascript, 
> core.async, and uses React directly for rendering (using Sablano for 
> templating).  I've adapted this basic approach with a few changes:
> 
> 1) Using React directly is fine, although you are relying solely on React's 
> DOM differencing for performance, with no IShouldUpdate optimizations like 
> you get with Om/Reagent.  To be honest, that's probably fine for most apps, 
> but I really like Reagent and wanted to simplify my templates a bit, so I 
> combined the Datascript approach with Dave Dixon's binding code (here: 
> https://gist.github.com/allgress/11348685).  This has worked fantastically!
> 
> So instead of monitoring the transaction stream and re-rendering the UI on 
> every change (as in the original article), individual Reagent components can 
> bind a datalog query to a reagent/atom and it will be updated whenever that 
> data changes in Datascript.  This works for arbitrarily complex queries and I 
> have been very pleased with the results in practice.
> 
> For example, in our app you can select an employee from a dropdown and  then 
> it will list work orders for the selected employee - a fairly common use 
> case.  This was trivial to implement with 3 queries - a query for the 
> selected employee, a query for all employees (to show in the dropdown menu) 
> and a query for all workorders assigned to the selected employee.  Each 
> Reagent component was built with its respective query bound to a r/atom, and 
> that's literally all there is to it.  When the selected employee changes, the 
> workorder query is automatically re-run and its atom updated, which triggers 
> Reagent to re-render the workorder list.
> 
> 2) So what about transient state - such as whether the dropdown is collapsed 
> or not?  This could be represented as component state easily in Reagent (or 
> Om, for that matter), but that brings up other issues.  For example, when a 
> dropdown menu is expanded, it's usually expected that clicking outside of the 
> menu will collapse it (we've all seen those annoying sites where you can only 
> close the menu by clicking on it again - UX Fail!).  
> 
> This is tricky to do with local state and it's easy to couple components that 
> really have no business knowing about each other.  Well thought out event 
> pipelines with core.async can solve this, but I try to be lazy whenever 
> possible.  So this transient state is kept in the DB as well.
> 
> There are a couple of ways to do this.  The easiest is to have a single "UI 
> state" entity and just lump all the transient state in there, and any 
> component needing access to it can just bind a r/atom to it.  I created a 
> bind-entity variant of the binding code to do just that - bind a r/atom to a 
> single entity and update whenever it changes.  What I ended up using is a 
> more granular approach, where each component can create its own entity to 
> track transient state and get a bound r/atom representing that state.  That 
> was easy to setup with a few helper functions.
> 
> 3) I'm using plain functions instead of core.async for event handling.  Just 
> a personal preference.  IMO, since the DB is the single source of authority 
> and any component can easily bind to the DB and react to changes, I found 
> that pub/sub was just unnecessary complexity (it could easily be added back 
> if I find I need it, though).
> 
> 4) I wasn't comfortable with having the Datascript DB (and core.async 
> channels, which I'm no longer using) as global state, so I created a quasi 
> component system for my app.  (NOTE to Stuart Sierra - PLEASE port component 
> to Clojurescript!  If you don't, I may just have to tackle it myself soon).  
> This adds some nice structure to app and is generally OK, but to be honest 
> I'm still not 100% convinced the additional complexity is worth it, at least 
> for smaller apps.  The main reason I stuck with it is because although this 
> current app is small-medium in size, and I could get away with not 
> structuring the code, then next phase of this project will entail building a 
> significantly larger app (hopefully reusing many of the components from this 
> app).
> 
> I still have mixed feelings about that last one.  Just having a global DB and 
> event bus that everything accesses certainly makes things MUCH easier, and 
> global state seems to be at least overlooked if not accepted in the 
> Javascript world.  But it just feels icky to me - 20+ years of professional 
> experience tells me that shortcuts like that come back to bite you in the a** 
> more often than not.  I would be interested in hearing others' opinions - 
> maybe I'm just being too "old school"?
> 
> Well hopefully someone finds this approach interesting.  Whether or not you 
> use core.async/functions or components/global-state, the basic approach of 
> Datascript + Reagent + bound queries is a BIG win in my book, and I've been 
> very happy with the results.  If people are interested, I've thought about 
> pulling out the key pieces into a demo app (minus the proprietary stuff for 
> my project), but it will probably be at least a couple weeks before I could 
> get to that.  In the meantime, if anyone has specific questions I can try to 
> answer and maybe pull out a few snippets as gists.

-- 
Note that posts from new members are moderated - please be patient with your 
first post.
--- 
You received this message because you are subscribed to the Google Groups 
"ClojureScript" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/clojurescript.

Reply via email to