How would one go about leveraging rad to render an form input field or subform, but outside a form body, just in a ordinary fulcro component? Is this even a valid thing to want? My usecase is that I have a multistep process in which the root entity is constructed in the first few steps via rad-form capabilities and then manipulated via mutations after that (for reasons). I would like to have the orchestration of this process to be done by a single component with a statechart so its easier the comprehend. What Im doing now is mounting the form-instance hidden, so I can obtain its instance and use that to get a form rendering-env, which I then use to render the input-fields/subforms inside my orchestrating component. But I find it hard to reason about whats exactly happening. Is this a valid pattern or are the better ones? And again, should I even want to do this or should I just render the first few steps inside the form and integrate that into the orchestrating component? (which I would find less preferable because I kinda like the clear overview of the current solution) some snippets:
{:keys [form-factory form-props]} (rad-hooks/use-form this GroupForm (tempid/tempid) save-group-complete cancel-save-group)
(dom/div {:style {:display "none"}} (form-factory form-props))
get-form-rendering-env (fn []
(when-let [form-instance (comp/class->any app GroupForm)]
(form/rendering-env form-instance form-props)))
(when-let [env (get-form-rendering-env)]
(form/render-input env group/group-name)))There are many answers I could give for this. Unfortunately the current RAD plugins all use UISM, so customizing the UISM on a form component might be your best bet. That would embed you in the right kind of env, and of course you can override the fo/machine, and hand-customize what gets rendered…once in a form, you can use all of the plugin bits like field renderers.
Ok, I'll look into that. I'm fine with experimenting with what structures benefit us and which don't. In that regard: should I expect any considerable downsides/issues from the pattern in the OP?
It seems lots of other people are seeing the desire/need for a statecharts version of RAD, which is on my “road map” in my head at least. The fulcro integration in statecharts is already started, and there is some work to do around the existing RAD components to make them compatible such that you could incrementally adopt new stuff without breaking the old. I’m personally torn on what to do, because I have limited time. A completely new overall system would be much easier to build (without mixed mode support)….but I think the only real problem is that RAD is currently written with hard core dependencies on dynamic-routing, and I’d want the routes to be controlled by charts. So, what I have in mind is to refactor the internals of RAD so that the routing isn’t tied to the dynamic routing ns, but is instead fronted by a protocol that can be implemented to use statecharts instead. But I have some R&D to do around how to make that work. You see, with a statecharts system, being IN a particular state should also force a UI route. statecharts could “control” via dr, but since dr allows for arbitrary routing, it would be hard to make the statechart itself “absolutely in control”. Furthermore, changes to the URL in browsers (and bookmarks) mean that there has to be a way to force such a chart into given states (or have it deny routing). I’ve build such a routing system in the statecharts lib already, but again it needs a bit more development, and in order to use it with RAD I would have to remove the hard links to dr. And of course I want to do all of this refactoring in a way that has no breaking changes to existing programs. Co-existence of the two modes is probably out, since routing the UI is a global concern and the URL a singleton. So, I guess since you’d either have to port to the new thing, or write from scratch, I think the proper thing to do is just generate a new set of namespaces in the RAD library that leverage statecharts. Then I don’t really have to touch the old code. Comment/suggestions/help is welcome.
Well, that thinking wasn’t right at all. Now that I’m looking at the code I realize that the UI plugin is very much dependent on a lot of things, and new namespaces approach won’t work if I want to maintain compatibility with existing rendering plugins. A number of the functions in the form and report nses have internal dependencies on the UISM nature of the existing beast. It’s a bit bigger job.
making it rad2 may be the safest, if many things break it would make it easier to migrate
yeah, but that means also making a new ui plugin, since they are kind of tightly coupled
Yes but is it really much more work than making a statecharts/state machine mix? It's definitely less complexity?
I’m actually going through right now and seeing what it would take.
is it expected that I can't change the :fulcro/aliases on my statechart without refreshing the page? I have this
(sch/use-statechart this {:session-id :QuestionForm
:data {:fulcro/aliases {:course-id [:actor/component :course/id]
:question/answers [:actor/component :question.single-choice/answers]}}})
and if I make any change to the aliases it's not reflected in (dom/div (str "ALIASES: " (pr-str aliases)))isn’t the statechart already running? If so, the data model is already populated. I don’t think hot-code reload should restart the chart, right?
We could discuss an API change/improvement where the aliases are stored somewhere else perhaps? But aliases should be something that is pretty darn static, so the answer is probably going to be at least restart the chart.
Yeah maybe the real question is what kind of workflow is efficient to develop and debug statecharts
exactly, and your data needs change slowly compared to step by step coding…
In defsc-form with use-statechart, when I do:
(rad-routing/route-to! app
QuestionForm
{:action form/create-action
:id (str form-id)
:initial-state {}})
(click of a "New" button)
the statechart doesn't unmount/remount and stays active with the previous Form as actor.
What am I doing wrong, is it a bug?Seems like there is unforeseen consequences I'm looking into it
When saving the id changes from tempid to actually id maybe it can cause issues?
Well, everything about a state chart is actually stored in the state map. Isn't it? Trmpid Rewriting is a global operation on that. I have a vague memory of that not actually being true 😕
If you look in the fulcro inspect database, is the temp ID left around as the session ID?
, I wonder if we rewrite any pending events in the state chart event queue.
So yeah, if the ident is used to derive the session ID, and a temp ID is involved, I may have missed some cases
I don't remember how I implemented the event queue.
I will get back to the bug later this week, it's a massive component and a massive statechart with some subcomponents having statecharts that talk back to the parent statechart so there is quite a lot to unpack there to really understand what goes wrong
but it went from "working" to getting stuck with that commit
but you said earlier “it works”. What then went wrong?
The form where I had to use a use-effect hook instead of use-statechart because it wasn't mounted on id change because the key was not set by fulcro
That is working I can use use-statechart now
But it broke that other component ik talking about in a way I don't understand yet
ok
Works thanks!
(hooks/use-lifecycle
(fn [] (println :mount-form))
(fn [] (println :unmount-form-id)))
I placed this at the top of the rendering body of the form, it never prints unmout-form when navgating from a form to a new form via the routingnever noticed that before maybe it's a react 19 thing too?
I realized it because I have a statechart in that form and on some event I do:
(scf/resolve-actor env :actor/component)
but it actually resolves to the previous form entityIs it the same component? The component has to unmount for lifecycle to fire
the defsc-form is in the router fo/route-prefix "question" fo/id question/id
and use-statechart call is in its body
so yeah it's never unmounted by react when you change the entity to a new one with rad/route-to
I ended up doing
(hooks/use-effect
(fn []
(scf/start! app {:machine ::QuestionForm
:session-id [::QuestionForm [:question/id question-id]]
:data {:fulcro/actors {:actor/component {:component ::QuestionForm
:ident [:question/id question-id]}}}})
(fn []
(scf/send! app [::QuestionForm [:question/id question-id]] :event/unmount)))
[question-id])don't really need the return value of use-statechart in the body of the form anyway and couldn't figure out how to pass it down to renderers so I do things like
(fn [{::form/keys [form-instance]}]
(let [props (comp/props form-instance)
ident (comp/ident form-instance props)
config (scf/current-configuration form-instance [::QuestionForm ident])] ...)
insteadTechnically the factory should be using the form’s ID as the react key, which should force react to unmount/mount when the ID changes 😕
oh, but you have it as a target in a router, and the router makes the factory then
Ah, so that is technically a bug in the dynamic router. It should make a keyfn for the target that uses that target’s ident.
I’ll push a patch
Try fulcro 76a6bdd08396dfd0b595149892041c968e7d9ccf