Description
So I'm getting started on my second major cljfx application, and this time around I'm trying to understand the machinery a bit better b/c I want to integrate some external JavaFX libs :))
I went through this super useful thing again https://overreacted.io/react-as-a-ui-runtime/
The React top level flow is:
- You have some elements (which you call
description
s) - The elements/descriptions go through reconciliation with the previous description tree to determine what's new/deleted/changed
- React updates GUI components and creates news one when necessary (in their case HTML, in ours JavaFX. I think you use the same term
component
) - User interaction can then trigger callbacks that lead to new state and therefore new/modified elements/
description
s.. and the cycle starts over
I think I grok how that all works. And the cljfx
README goes through a logical progression introducing the concepts incrementally.
- manually turning
description
s into JavaFXcomponent
s and then drawing them to screen
(fx/on-fx-thread
(fx/create-component
;; map describing the GUI
))
- have the user action generate a new
description
and using arenderer
do reconciliation and reuse the GUIcomponent
s
{
;; map describing the GUI
:on-action (fn [_]
(renderer
;; feed in a new map
))
}
- You now introduce a state atom and you use
fx/mount-renderer
to have the renderer get called automatically when the state changes. So far so good! .. But then there is this whole extra stuff .. something withmiddleware
andlifecycle
...
(def renderer
(fx/create-renderer
:middleware (fx/wrap-map-desc assoc :fx/type root)))
This is the part where I get a bit lost. On a high level, I don't really understand why we need additional machinery at this point. Why can't we just:
- deref the state atom,
- generate a new GUI
description
tree - call
renderer
on it
I'll note that the linked React intro doesn't use the term lifecycle
at all - so this isn't slotting itself into my mental model I guess.
a. I've tried running without the middleware and of course it doesn't work at all. If you call renderer
you can pass in a desc
map, but when you want the :on-value-change
lambda to trigger the state change, then the renderer is unhappy. It wants a lifecycle (I'm currently trying to poke around and understand why - but my callstack is strange at the crash point)
b. I tried to use my own middleware fn
that would print the lifecycle - so I can actually find out what the heck a lifecycle is. But this 'fn` is only run once on launch .. :) (this confuses me further.. then why do we need this lambda if it's seemingly not used?)
c. I tried to look through your code, and it's basically the desc
map with some metadata attached - that specifies how to create/update/remove the .. internal GUI tree? (I think?). I've kinda understood "lifecycle" to be some intermediary between the element/description and the component. Because the reconciliation needs to be done on a different abstraction level than the component?
Is the lifecycle the UI component
tree and some associated update functions internal to the GUI framework?
You previously explained it to me as:
#34
"Lifecycle is a behavior that manages how things get created from description, changed to other value from previously created when description changes, and destroyed when description no longer exists. "
But I unfortunately don't really understand what "Lifecycle is a behavior" means in concrete terms
Hope you can help me get over this conceptual hurdle. I'd be glad to try to help flesh this out in the README once I get it :))