Can you elaborate why you implement a new IDeref? Why not just calculate and
pass the derived state during rendering? Memoization in any fashion could still
happen.
Also, is memoize-last just intended as an optimzation to consume less memory,
or does it serve another purpose?
On Sunday, February 15, 2015 at 11:58:17 PM UTC+1, Oliver George wrote:
> My approach to this stuff is to think in terms of state and derived state.
>
>
>
> I have a few helpers to facilitate implementing that which amounts to
> Use an atom for stateUse a function to derive stateUse a simple cache to
> reduce load of derivation in render loopHack deref to make atom pass back
> derived-state
> If you're new to OM I'd encourage you not to get carried away with picking up
> dodgy bits of code like the ones I'm sharing here until you're confident
> you're making best use of OM as it comes out of the box.
>
>
> That being said...
>
>
> Here's my derived-atom! function.
>
>
>
>
> (defn derived-atom!
>
>
> "
>
>
> This allows us to access derived values associated with atom state.
>
>
>
>
>
>
> The implementation of atom does not use deref so we aren't interfering
>
>
> with how state transitions, they use .-state to access the pure attribute
>
>
> value.
>
>
>
>
>
>
> The implementation of om ref cursors use deref to resolve the current value
>
>
> of state so we are able to apply our logic and have the result available
>
>
> via (value c) which means reference cursors will trigger updates based
>
>
> on changes to derived data too.
>
>
> "
>
>
> [iref derived-fn]
>
>
> (specify! iref
>
>
> IDeref (-deref [_] (derived-fn (.-state iref))))
>
>
> iref)
>
>
>
> This is how i used it as my om app-state
>
>
>
>
> (defonce app-state (derived-atom! (atom {}) (memoize-last derived-state)))
>
>
>
> memoize-last is a cutdown version of memoize which just remembers the last
> version. That essentially means that we only calculate derived-state once
> for each OM state
>
>
>
>
> (defn memoize-last
>
>
> "Returns a memoized version of a referentially transparent function.
>
>
>
>
>
>
> The memoized version of the function keeps a cache of the *most recent call*
>
>
> from arguments to result.
>
>
> "
>
>
> [f]
>
>
> (let [mem (atom {})
>
>
> lookup-sentinel (js-obj)]
>
>
> (fn [& args]
>
>
> (let [v (get @mem args lookup-sentinel)]
>
>
> (if (identical? v lookup-sentinel)
>
>
> (let [ret (apply f args)]
>
>
> (reset! mem {args ret})
>
>
> ret)
>
>
> v)))))
>
>
>
> Finally, here's me using derived state logic to do things like lookups,
> validation and other things. It's all very app specific but amounts to doing
> (assoc-in state [:a :b] :something) a lot. At this risk of confusing things
> here's my generalised required field validation logic which is implemented as
> derived state.
>
>
>
>
> (def empty-values #{nil "" [] {} #{}})
>
>
>
>
>
>
> (defn validate-required-field [field]
>
>
> (let [{:keys [required value errors]} field]
>
>
> (if (and required (contains? empty-values value))
>
>
> (assoc field :errors (conj errors "This field is required"))
>
>
> field)))
>
>
>
>
>
> (defn is-required-field?
>
>
> "Identifies walker nodes which are fields relevant to require logic"
>
>
> [m]
>
>
> (and (map? m)
>
>
> (contains? m :required)
>
>
> (contains? m :value)))
>
>
>
>
>
>
> (defn validate-required-fields
>
>
> "Derive errors associated with missing required fields"
>
>
> [state]
>
>
> (postwalk
>
>
> #(if (is-required-field? %) (validate-required-field %) %)
>
>
> state))
>
>
>
>
>
>
> (defn derived-state
>
>
> "Used to include derived state for use by components."
>
>
> [state] (-> state
>
>
> ;derive-vertical-required
>
>
> ;end-position-logic
>
>
> ;maint-freq-logic
>
>
> validate-required-fields))
>
>
>
>
>
>
>
>
>
>
>
> On Mon, Feb 16, 2015 at 4:17 AM, Leon Grapenthin <[email protected]> wrote:
> If they were changing, you could implement your own reference system, where
> you have one stateful lookup map that you make globally available via a ref
> cursor. Instead of the actual changing values, you'd use lookup keys in your
> rendered cursors. Changes to values in the stateful lookup map would would
> reflect everywhere they are looked up.
>
>
>
>
>
> On Sunday, February 15, 2015 at 3:54:36 PM UTC+1, Scott Nelson wrote:
>
> > I’m working on an Om application that includes a music browser and player
> > with a simple artist -> albums -> songs hierarchy. When a user is viewing
> > an artist section I layout all the artist’s albums and songs hierarchically
> > and this works great since there is basically a 1-to-1 correspondence
> > between the component hierarchy and the data hierarchy.
>
> >
>
> > Here’s a basic example of what a piece of the application state might look
> > like under some sort of :current-artist key:
>
> >
>
> > {:name “The Beatles”
>
> > :albums [{:name “Abbey Road”,
>
> > :songs [{:name “Come Together”}
>
> > {:name “Something”}]}
>
> > {:name "Rubber Soul",
>
> > :songs [{:name "Drive My Car"}
>
> > {:name "Norwegian Wood"}]}]}
>
> >
>
> > When a user plays one of these songs I build an ordered playlist of all the
> > artist’s songs. There is a player component that plays the playlist (even
> > if the user navigates away from the current artist section) and displays
> > the currently playing artist, album and song name. To achieve this I ended
> > up duplicating a bunch of artist/album/song data elsewhere in the
> > application state.
>
> >
>
> > Here’s what the :playlist path of the application state might look like
> > after a user clicked the play button next to “Drive My Car”:
>
> >
>
> > {:play-index 2
>
> > :songs [{:name “Come Together”
>
> > :album {:name “Abbey Road”
>
> > :artist {:name "The Beatles"}}}
>
> > {:name “Something”
>
> > :album {:name “Abbey Road”
>
> > :artist {:name "The Beatles"}}}
>
> > {:name “Drive My Car”
>
> > :album {:name “Rubber Soul”
>
> > :artist {:name "The Beatles"}}}
>
> > {:name “Norwegian Wood”
>
> > :album {:name "Rubber Soul"
>
> > :artist {:name "The Beatles"}}}]}
>
> >
>
> > I think this sort of denormalization works fine in my case since the
> > artist/album/song data is not changing but I'm wondering if there is a
> > better way to accomplish this. If the data were changing then I would
> > imagine it could become a challenge to keep the playlist data in sync.
>
>
>
> --
>
> 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.
>
>
>
>
>
> --
>
>
> Oliver George
> Director, Condense
> 0428 740 978
--
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.