fulcro

Yab Mas 2025-09-04T09:50:45.252719Z

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)))

tony.kay 2025-09-04T10:41:50.970999Z

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.

Yab Mas 2025-09-04T10:46:55.604929Z

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?

tony.kay 2025-09-04T10:49:00.365229Z

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.

❤️ 1
tony.kay 2025-09-04T11:41:26.185579Z

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.

Eric Dvorsak 2025-09-04T12:16:18.352949Z

making it rad2 may be the safest, if many things break it would make it easier to migrate

tony.kay 2025-09-04T16:40:16.230559Z

yeah, but that means also making a new ui plugin, since they are kind of tightly coupled

Eric Dvorsak 2025-09-04T16:50:19.887019Z

Yes but is it really much more work than making a statecharts/state machine mix? It's definitely less complexity?

tony.kay 2025-09-04T16:51:56.917469Z

I’m actually going through right now and seeing what it would take.

Eric Dvorsak 2025-09-04T15:19:15.483079Z

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)))

tony.kay 2025-09-04T16:31:06.909159Z

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?

tony.kay 2025-09-04T16:32:14.654909Z

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.

Eric Dvorsak 2025-09-04T16:48:37.390069Z

Yeah maybe the real question is what kind of workflow is efficient to develop and debug statecharts

tony.kay 2025-09-04T16:52:28.892579Z

exactly, and your data needs change slowly compared to step by step coding…

Eric Dvorsak 2025-09-04T20:50:35.670099Z

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?

Eric Dvorsak 2025-09-09T08:41:19.628229Z

Seems like there is unforeseen consequences I'm looking into it

Eric Dvorsak 2025-09-09T11:23:30.252979Z

When saving the id changes from tempid to actually id maybe it can cause issues?

tony.kay 2025-09-09T16:39:02.152949Z

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 😕

tony.kay 2025-09-09T16:39:57.162359Z

If you look in the fulcro inspect database, is the temp ID left around as the session ID?

tony.kay 2025-09-09T16:40:10.459619Z

, I wonder if we rewrite any pending events in the state chart event queue.

tony.kay 2025-09-09T16:40:31.376799Z

So yeah, if the ident is used to derive the session ID, and a temp ID is involved, I may have missed some cases

tony.kay 2025-09-09T16:40:47.009689Z

I don't remember how I implemented the event queue.

Eric Dvorsak 2025-09-09T16:55:20.113809Z

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

Eric Dvorsak 2025-09-09T16:55:31.810999Z

but it went from "working" to getting stuck with that commit

tony.kay 2025-09-09T17:37:58.313179Z

but you said earlier “it works”. What then went wrong?

Eric Dvorsak 2025-09-09T18:34:16.885879Z

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

Eric Dvorsak 2025-09-09T18:34:30.625309Z

That is working I can use use-statechart now

Eric Dvorsak 2025-09-09T18:34:58.681569Z

But it broke that other component ik talking about in a way I don't understand yet

tony.kay 2025-09-09T18:51:49.712359Z

ok

Eric Dvorsak 2025-09-07T12:39:48.105849Z

Works thanks!

Eric Dvorsak 2025-09-05T05:19:14.514859Z

(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 routing

Eric Dvorsak 2025-09-05T05:19:30.883059Z

never noticed that before maybe it's a react 19 thing too?

Eric Dvorsak 2025-09-05T07:15:00.044829Z

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 entity

tony.kay 2025-09-05T15:27:13.456959Z

Is it the same component? The component has to unmount for lifecycle to fire

Eric Dvorsak 2025-09-05T15:29:57.233759Z

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

Eric Dvorsak 2025-09-05T15:30:16.964989Z

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])

Eric Dvorsak 2025-09-05T15:31:15.046839Z

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])] ...)
instead

tony.kay 2025-09-06T06:24:18.619279Z

Technically the factory should be using the form’s ID as the react key, which should force react to unmount/mount when the ID changes 😕

tony.kay 2025-09-06T06:25:38.789189Z

oh, but you have it as a target in a router, and the router makes the factory then

tony.kay 2025-09-06T06:27:24.378629Z

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.

tony.kay 2025-09-06T06:34:30.541889Z

I’ll push a patch

tony.kay 2025-09-06T06:37:11.198789Z

Try fulcro 76a6bdd08396dfd0b595149892041c968e7d9ccf