manutter51 / woolybear

Demo for Clojure Conj 2018
69 stars 11 forks source link

Questions #1

Open lwhorton opened 5 years ago

lwhorton commented 5 years ago

Sorry in advance for making this an issue; I'm not really sure how else to reach you, but I also think the following might be useful for the public domain.

Mark, great talk!

I've been using re-frame on/off for over 3 years now. It's a fabulous front end paradigm, and I'm glad someone decided to dedicate conference time to the topic.

I have never really been satisfied with any sort of reusable component scheme that I've invented or found in the wild. There seems to be an inevitable tax to be paid with respect to unidirectional functional components and reusability. The elm language and elm architecture have been debating this topic for a very long time. Some approaches out of that world satisfy the "infinite abstract reusability" problem (can achieve any N number of components without any component having "a place"), but at the cost of scattered state (no single app-db, as re-frame would have it). Some approaches out of the re-frame world can achieve the same goal but with what feels to me like too much manual effort or "hand-holding".

Your DRY approach to reducing handlers/subs is neat and seems like a step in the right direction to semi-autonomous components and low-effort reusability. However, something that immediately strikes me is how inflexible this might become over time. If each component is self aware enough to "know its slice in the db", it seems to be quite literally PLOP-oriented programming. To highlight a concrete problem: how do you handle the inevitable refactoring that stems from an evolution of an application (whether from changing requirements, better designs, better domain knowledge, etc.) if "places" are littered everywhere throughout the app? I find my app-db shape/schema changes quite regularly as the application evolves. Through judicious use of subscriptions and the great idea of inject-sub I am able to more-or-less keep any notion of "where" located to a single location (per component, or per feature). The proliferation of "wheres" everywhere makes me nervous, but the drastic reduction in boilerplate feels safer and less annoying.

Thoughts?

manutter51 commented 5 years ago

Yes, I did give some thought to that very problem but unfortunately I didn't have time to discuss it during the talk. My approach (which is probably somewhere between a workaround and a real solution) is to use path-building functions to achieve a kind of relative addressing. See for example, the path-to function in the registration form:

https://github.com/manutter51/woolybear/blob/master/src/cljs/woolybear/packs/forms/registration_form.cljs#L29

The idea is that I'm going to use reusable form field components in my registration form. The path-to function gives the path leading up to the form itself, and then each form field within the form knows that, say, "I am the :username field" or "I am the :password field". So each field can pass in its own "relative address" (i.e. the key for just the field itself), and the path-to function will return the rest of the path. To move my registration form to a different area within the app-db, I just need to update the path-to code, and the fields will automatically be updated to the new location. Not perfect, but a bit more DRY than having each component know its own absolute path.

Definitely open to new ideas/alternatives/etc, so I'll leave this issue open for a while. Let me know what you think.

manutter51 commented 5 years ago

By the way, the path-to function is used in the mk-field-attributes function which follows it. This is a factory function to generate the "opts" map for each of the fields, given the field key. A nice DRY approach, but probably not a wise thing to do in tutorial-type code, since it kind of obscures the fact that I'm having each separate form field make a call to path-to for the path to its component data. My bad.

lwhorton commented 5 years ago

This seems certainly more sensical to me than just random key vectors floating around. Generally, though, I organize my entire db into entities and feature-specific state (forms, mostly, then usually some navigation state). I guess there's not a huge difference between :forms {:feature-a {:form-a ...} :feature-b {:form-a ...} and :feature-a {:form-a ...}, :feature-b {:form-b ...}, but it does seem somehow less clean. At this depth of detail these decisions are kind of trivial, though. I'll spend some more thought on the higher level problem and see if anything else interesting comes up.