fulcro

roklenarcic 2026-06-04T07:42:10.124099Z

What’s the recommended pattern on how to steer statecharts? Currently in transition elements I usually have a script and that script does some decision logic and calls senv/raise . Do you do the same or do you like to roll it out more explicitly, mutating working memory and then using If elements to inspect sc state and raise elements to switch states? I think I might be using charts too thinly, lots of logic is in script functions so that’s opaque from chart perspective.

roklenarcic 2026-06-04T07:43:05.489459Z

Should I try to use scripts more to transfer/extract data from scf/mutation-result to working memory and then run elements on that?

tony.kay 2026-06-04T09:59:10.759719Z

in general mutations should use returning and target so that there’s no logic to do on that front. If there’s a single fact that you can point to that represents something, then duplicating that fact as a state is generally a bad idea, but in general I guess the way I think about them is “what is the process that I need to track that I would otherwise spread out in callback hell or hairballs of dijointed logic? It’s and organizational tool. I want to see what my system does, not have to puzzle it together. So, in my subscription system for example, I’m keeping track of things like “fully paid” because otherwise I have to analyze invoices and payments. I track grace periods on late payments with a state and a delayed event, etc. The payment processing gateway (which is disconnected mess of UI, server db interactions, webhooks, etc.) needs a few states and timed events, etc. But when you get too low into the weeds you may be making more in the statechart than is necessary, or might be easily represented by a simple, say, ui flag.

tony.kay 2026-06-04T10:00:21.612319Z

The reason I never write generic form or report logic at all anymore is because all of the generic process logic for what you do in those is easy to write-once as an abstraction over the the data integration of Fulcro itself already has.

tony.kay 2026-06-04T10:01:07.608689Z

Which IMO is one of the cooler things that “popped out” of the overall design (UI State Machines and now those same things applied to statecharts).

roklenarcic 2026-06-04T10:29:10.974989Z

Thanks for explanation.

roklenarcic 2026-06-04T11:16:25.233869Z

I’ve got a chart that runs a process and different callers start it. I need a callback like mechanism so the code that started it can do some refreshes when the chart ends, but it’s different every time based on the context of the caller. How do I most easily achieve that?

tony.kay 2026-06-04T18:37:08.757979Z

This is what invocations are about. Implement the InvocationProcessor and install it.

roklenarcic 2026-06-04T18:37:43.979379Z

do you mean to write my own InvocationProcessor that supports a callback?

tony.kay 2026-06-04T18:38:26.706429Z

Not exactly. Invocations are exactly what they sound like, but the communicate via events, and the protocol is the adapter. See SCXLM specification and statecharts docs.

tony.kay 2026-06-04T18:38:50.782889Z

callbacks are exactly what you want to avoid, right?

tony.kay 2026-06-04T18:39:10.834049Z

you want events coming into the context of the chart, where they can be handled by the chart

roklenarcic 2026-06-04T18:39:18.597819Z

I mean yea I know how statecharts communicate, but I am starting this one outside of any statecharts, so there’s nothing to get back

tony.kay 2026-06-04T18:39:30.801909Z

you also want a “called thing” to terminate when you’re no longer interested in that call running

roklenarcic 2026-06-04T18:40:01.180989Z

I mean usually if you are already in sc, you start a chart and you get events back or whatever. But if I am not in sc?

tony.kay 2026-06-04T18:40:19.280529Z

What?? You mean you have an external process that needs to talk to the chart? It sends events to the session id via the external event queue

tony.kay 2026-06-04T18:40:35.007109Z

which it can take as a parameter when you start it

roklenarcic 2026-06-04T18:41:45.494809Z

no it’s just a on-click handler, starts a chart with scf/start!, at some point that process completes, and I want to run some refresh operations. The precise nature of operations is known at the point of this scf/start! call rather than in the chart context. i.e. in another context a different thing needs to happen when this chart completes

roklenarcic 2026-06-04T18:42:19.517689Z

maybe I’m coming at this wrong

tony.kay 2026-06-04T18:56:19.369559Z

Personally I’m constructing my UI completely from statecharts. In your case you’re using them more selectively, so you need to invent your glue point. You could certainly pass a lambda into the initial data of the chart on start.

roklenarcic 2026-06-04T18:56:59.665999Z

hm I guess lambda works even though it’s not serializable?

tony.kay 2026-06-04T19:04:41.459379Z

Are you serializing your charts or state?

tony.kay 2026-06-04T19:05:01.903449Z

Certainly if you were using a support viewer or including them on mutation params you’d have a problem…

roklenarcic 2026-06-04T19:33:15.031609Z

not at this time, I am just staying conservative in case I need this later. Do you put things like lambdas in your statecharts?

tony.kay 2026-06-04T20:02:02.679169Z

other than as expressions for script elements? Not generally. I tend to follow the “serializable” rule because things like working memory in my sever-side statecharts gets written to SQL db when “idle” (between external events). So, no, I don’t tend to store anything but data in my events/working memory…but remember that EQL (which includes mutations) are themselves data. So, putting a mutation with params as data (a vector containing a list that starts with a symbol…) is the Fulcro equivalent of storing a ” serializable callback” which can be invoked via transact!

roklenarcic 2026-06-04T12:00:52.269349Z

Some advice on idents… Most UI components I’ve got have idents like:

[:component/id ::Table]
But the backend resolvers have keys like:
::user-list
So pretty much all of the time I have to do the targetted load where I specify UI ident, then the server ident, then the target vector of where to put the thing. Seems kinda redundant. Does anyone have a different approach that would be more ergonomic?

tony.kay 2026-06-04T18:36:18.032799Z

I use:

{
 ;; Always use 2 spaces for indentation (equivalent to "Default to Only Indent")
 ;; This applies [:inner 0] to all forms, giving consistent 2-space indentation
 :indents                         ^:replace {#".*" [[:inner 0]]}

 :align-map-columns?              true
 :indentation?                    true
 :remove-surrounding-whitespace?  true
 :remove-trailing-whitespace?     true
 :insert-missing-whitespace?      true
 :remove-consecutive-blank-lines? false
 }

roklenarcic 2026-06-04T18:37:04.903749Z

unfortunately not everyone on the team agrees with my flat 2 space indent

roklenarcic 2026-06-04T18:37:11.658469Z

I used to run that too

tony.kay 2026-06-04T18:43:47.496089Z

You could make a pathom resolver that treats certain kinds of idents reflectively (e.g. :component/id) and just do load with that ident as the server key

(df/load! this [:component/id ::Table] Table)

tony.kay 2026-06-04T18:47:38.878009Z

or even write a wrapper to make it even shorter:

(defn load-component! [this cls]
  (df/load! this (comp/get-ident cls) cls))

tony.kay 2026-06-04T18:48:03.984559Z

or using the statecharts primitives if you’re in there

roklenarcic 2026-06-04T18:51:03.297649Z

I guess this could work

tony.kay 2026-06-04T19:06:01.551939Z

or you could handle the ident case without the pathom resolver with a wrapper as well, so less invasive

tony.kay 2026-06-04T19:06:58.229349Z

(defn load-global-as-field! [this cls field]
  (df/load! this field cls {:target (conj (get-ident cls) field)}))

tony.kay 2026-06-04T19:07:54.602029Z

This is why I didn’t go (more) nuts on the load system. In fact, if I had it to do over again I’d have started with statecharts and load would not have post-action, post-mutations, etc.

roklenarcic 2026-06-04T22:33:14.805749Z

Well I liked the idea that I could issue a load of the whole page and the query abstraction would automatically result in an EQL query that I could process on the server, but this split between the query on the client and the the query on the server kinda kills it. So I just issue loads for individual bits of data when I need it, but now this isn’t much different than REST APIs

tony.kay 2026-06-04T19:08:28.912409Z

ask Claude?

roklenarcic 2026-06-04T21:45:34.641649Z

this should work?

(afop/load ::second-fa/credentials :actor/list {:target [:actor/list ::second-fa/credentials]})
I would think that actor could be used in target vector like this, but I get this warnings:
2026-06-04T21:44:56.856Z WARN [com.fulcrologic.fulcro.data-fetch:108] - Data load targets of two elements imply that you are targeting a table entry. That is probably incorrect. Normalization targets tables. Targeting is for creating missing edges, which are usually 3-tuples. See 

tony.kay 2026-06-04T21:49:25.386769Z

look at the docs/source…don’t remember. In UISM I made separate arg names for ones that support actors to prevent accidents, but don’t remember to be honest