fulcro

Eric Dvorsak 2025-09-11T08:14:00.213649Z

> Imagine a form. The ident for a new thing has a tempid. The ident for the saved version does not. Now you have a moving target. @tony.kay that is what state machines do right? before/after

tony.kay 2025-09-11T08:20:02.341949Z

You’re making me realize that I need to double-check that I’ve implemented the event queue properly for statecharts integration so that the data in delayed events supports tempid rewriting. Otherwise things are broken with respect to tempids.

tony.kay 2025-09-11T08:21:43.497489Z

I don’t mind the idents in the state database, event queues, etc. from updating with a tempid rewrite. That makes sense in the overall data model. What I don’t love is the identity of a particular session changing over time. The identity of some entity changed. The session of the statechart is still what it was. I don’t love coupling the convenience with something that breaks with semantics.

tony.kay 2025-09-11T08:22:21.902739Z

I can probably argue myself into it…but that’s my general resistence. I’m in library writer mode, and you have to be very picky so as not to confuse people/thing/internals

tony.kay 2025-09-11T08:24:03.843179Z

tempids have a particular problem that you have to cope with, that generally doesn’t bite you, but can: closures. If you close over a tempid in a programming sense, no tempid rewriting can help you. So, if I’ve closed over the session id (setTimeout (fn [] …send to sessionid that has tempid…) 1000), which anyone might code, now the session ID being a moving target is a bad thing.

tony.kay 2025-09-11T08:24:42.881539Z

the semantics of a session id are that it will be stable. The semantics of a “new entity” are that it’s ID is inherently unstable. YOu can still get into trouble, but I feel less sorry for you in the latter case 🙂

Eric Dvorsak 2025-09-11T08:26:47.701409Z

yeah I'm with you, I guess I would rather have random session-ids if the utility functions around statecharts are convenient enough. what I really don't like is hardcoded session-ids that are known at compile time, because that means they are a singleton and only in rare cases (eg a FileDownloadManager) do I want that. I already had a case of a delayed event affecting a new instance of a statechart on a different entity due to a hardcoded sessionid

tony.kay 2025-09-11T08:27:56.106189Z

OK, back to that. Why do you think the session IDs are constant? Read ro/initialize and ro/initial-props.

tony.kay 2025-09-11T08:28:51.216769Z

hold on…let me refresh my own memory

tony.kay 2025-09-11T08:30:18.579339Z

I’m lost on which thing we are talking about. The UI Routing statechart has a constant, well-know session ID. The istate routes allows you to specify the child session ID…and since only one instance of that particular child can be active at a time, why is that a problem?

Eric Dvorsak 2025-09-11T08:30:55.609099Z

I mean one can provide a constant one, which is what we do because oftentime we havent figured out a way to talk to the statechart otherwise

tony.kay 2025-09-11T08:31:23.714029Z

autoforward, but that’s messy IMO

tony.kay 2025-09-11T08:32:46.019489Z

So, what exactly are you proposing? I need a session ID before I can start the invocation. If the invocation is going to do work that sets up the target, I don’t know the ident yet. Chicken-and-egg. An abstract (but constant) ID you can set just solves the problem (from an external actor standpoint). But autoforward can actually be done well, now that I think about it.

tony.kay 2025-09-11T08:33:57.508839Z

If you properly namespace your events to the child chart, then just autoforward and send them to the routing chart 😄

Eric Dvorsak 2025-09-11T08:34:03.579589Z

so without use-statechart there's: • current-invocation-configurationsend-to-self current-invocation-local-data would be nice but then don't you need to manually add the configuration and the local-data of the statechart to the query?

tony.kay 2025-09-11T08:35:12.977319Z

Given the actor model, the point is that any component-centric data would be placed on the component state and query, and aliased into the chart. The internals of the statechart should be irrelevant to the UI

tony.kay 2025-09-11T08:36:03.063499Z

But yeah, for convenience it’s not terribly hard to supply those, but yes, it you want them to update properly, you have to add somethign to the query.

Eric Dvorsak 2025-09-11T08:36:55.353509Z

makes sense, what's the point of local-data then?

tony.kay 2025-09-11T08:37:09.870099Z

implementation details

tony.kay 2025-09-11T08:37:32.791139Z

e.g. in RAD reports it keeps cached info about the current loaded data

tony.kay 2025-09-11T08:37:39.783259Z

none of that is used by the UI

Eric Dvorsak 2025-09-11T08:37:41.894109Z

that would save the verbose aliasing

tony.kay 2025-09-11T08:38:47.959249Z

the point of actor/aliasing is two things: • Chart re-use. • Separation of implementation detail from UI-visible information

tony.kay 2025-09-11T08:40:43.488489Z

Remember that a chart can have many actors. An alias just gives you a shorthand for a combo of a particlar actor fact.

tony.kay 2025-09-11T08:41:14.062019Z

otherwise your API would be “assign :ui/flag? on actor X”

Eric Dvorsak 2025-09-11T08:41:15.229329Z

yeah but for colocated statecharts that have :actor/component automatically added and are invoked by a parent istate

tony.kay 2025-09-11T08:41:34.310079Z

but they can add additional actors…that component might have children that are part of the story

Eric Dvorsak 2025-09-11T08:41:50.506519Z

right

tony.kay 2025-09-11T08:42:10.961529Z

The convenience is I supply one for you 😄

Eric Dvorsak 2025-09-11T08:44:28.852509Z

ok I think I get the idea

Eric Dvorsak 2025-09-11T08:45:04.574719Z

but shouldn't we still have the statechart config in the query when relying on current-invocation-configuration result in the component?

tony.kay 2025-09-11T08:45:15.545619Z

yes

tony.kay 2025-09-11T08:45:19.161419Z

as shown in the demo code

tony.kay 2025-09-11T08:45:43.598399Z

tony.kay 2025-09-11T08:46:28.586449Z

I generally tell people that a link query is probably OK. It may cause a little over-rendering, but in practice it should be fine.

Eric Dvorsak 2025-09-11T08:46:49.294309Z

yeah that's what I was worried about 😄

tony.kay 2025-09-11T08:47:10.996559Z

E.g. [::sc/session-id '_]

Eric Dvorsak 2025-09-11T08:47:35.377599Z

you'd cause a rerender on any statechart change right?

tony.kay 2025-09-11T08:47:38.775739Z

yes

tony.kay 2025-09-11T08:48:10.506129Z

but I don’t bother optimizing that unless it actually proves to be a problem. This stuff is surpisingly fast in most cases.

Eric Dvorsak 2025-09-11T08:48:45.781529Z

if you systematically use statecharts in all of your components you'd be re-rendering the whole screen on every keystroke?

tony.kay 2025-09-11T08:48:47.849309Z

Remember that the thing changing is changing because it is already on-screen 😄

tony.kay 2025-09-11T08:49:04.571219Z

and there usually aren’t a lot of different components on screen at once

tony.kay 2025-09-11T08:49:22.173019Z

so the probability that you’re re-rendering “the right thing” is very very high

tony.kay 2025-09-11T08:50:22.037339Z

using the !! variants of prop changing focuses render to the local component…so there’s one possibly optimization for the cases that are forms (form field change fast local render).

tony.kay 2025-09-11T08:51:07.564959Z

If you have some weird UI where you have a statechart hammering timed events at a high frequency, it would be a problem…but otherwise I think you’re pre-optimizing for no good reason.

Eric Dvorsak 2025-09-11T08:51:48.984929Z

I'm not trying to pre-optimize just understanding what corner I'm potentially putting myself into 😄

tony.kay 2025-09-11T08:52:27.868469Z

yeah, I’m saying that if you need the configuration of a chart, putting a table link in the query probably isn’t going to hurt anything in 99% of cases

Eric Dvorsak 2025-09-11T08:52:43.203549Z

I can also not use the statechart config and reflect the relevant states as a ui prop

Eric Dvorsak 2025-09-11T08:53:05.629489Z

eg if I have a loading state assign :ui/loading? true

tony.kay 2025-09-11T08:53:43.331669Z

yes…there’s something to be said for not “denormalizing” things. I would not make states for things the chart isn’t in control of. For example, “dirty/clean” is represented in form-state, and should not have two states in the chart.

tony.kay 2025-09-11T08:54:38.047209Z

loading? could make sense as states, but you are right that manipulating a UI representation of that in the on-entry bits of the chart probably makes more sense, since there may be a number of states related to “being busy” that all should manifest the same way in the UI. E.g you could have a complex chart where loading include retries and such. Splitting out things that you want to display as logic in the chart makes the UI much simpler…

tony.kay 2025-09-11T08:56:03.169269Z

and much simpler UI with less logic is exactly what I’m going for.

tony.kay 2025-09-11T08:57:06.428619Z

I’m leaning very much in the direction of “coupling the UI to the internal structure of a chart is a poor design”

tony.kay 2025-09-11T08:57:45.745589Z

Instead: • Clearly state (via aliases) the things you need to know to properly render the UI • Write a statechart that produces that data • In cases where the data in the fulcro state database already represents some “state”, don’t re-model that in the chart (e.g. form dirty is a fact derived from data, not structure)

Eric Dvorsak 2025-09-11T09:00:59.369799Z

yeah I agree, and I think to make adoption easier having guidelines like this help

tony.kay 2025-09-11T09:03:07.537169Z

My time is limited. Documentation will eventually pop out, but I’m still refining my own idea of what the guidelines are…you’re helping 😄

tony.kay 2025-09-11T09:03:39.201529Z

My intuition tells me that this will be a great way to build large systems…but since “this way” doesn’t exist yet, I’m not sure how that is going to pan out.

Eric Dvorsak 2025-09-11T09:04:11.231109Z

> you’re helping 😄 thanks yeah I'm trying 😄

Eric Dvorsak 2025-09-11T09:06:01.427119Z

My intuition tells me that this will be a great way to build large systemsyeah I agree, in that regard I think use-statechart was a great hook to get things rolling on our side, but I get the feelings that it's not right it encourages the use of local-state and config in the UI which I now feel is wrong it only lets you use the colocated chart and its not even the same as the uir option

tony.kay 2025-09-11T09:08:02.414299Z

So, the good things I like is that a complete system modeled with the routes in a single chart give you the ability to reason about your routing globally. This is great for URL integrations, and things like the use of the statechart history node. I’m hoping that the invocation composition helps with teasing out minutiae so that the top-level chart doesn’t get too insane. Every route is going to need a top-level transition (generated by the wrapper function), but visualizing that means a LOT of edges into every possible target node.

tony.kay 2025-09-11T09:10:21.188919Z

I like that it gives a much better model around I/O and side-effects as well for the complexities of moving around in an application. The current dynamic routing system decouples all of the elements in your system where it is harder to reason about global concerns. DR also isn’t even written quite right and couples some concerns to the actual on-screen render…which is horrible 🙈

tony.kay 2025-09-11T09:14:30.930859Z

With DR you can kind-of get the “history node” thing, but it’s a bit of a mess to implement (e.g. I switch from Inventory to Sales, but Sales has 3 sub-routes…can I remember which one I was on last time I was there)

Eric Dvorsak 2025-09-11T09:14:59.156609Z

yeah in the exemple you have everything in the same chart:

(def application-chart
  (statechart {}
    (uir/routing-regions
      (uir/routes {:id           :region/routes
                   :routing/root Root}
        (uir/rstate {:route/target `RouteA1})
        (uir/rstate {:route/target `RouteA2}
          (uir/istate {:route/target     `RouteA21
                       :exit-target      ::RouteA1
                       :child-session-id ::route-a21})
          (uir/rstate {:route/target `RouteA22}))
        (uir/rstate {:parallel?    true
                     :route/target `RouteA3}
          (uir/rstate {:route/target `RouteA31})
          (uir/rstate {:route/target `RouteA32}))))))
in my use case at the moment I'm integrating it isolation within a modal to navigate the wizard. I actually broke down the substates into their own components and statecharts

tony.kay 2025-09-11T09:15:10.191049Z

routing to the “same component” but “different identity” is also a case that bites people

tony.kay 2025-09-11T09:16:56.449779Z

Yes, I am right now working on the ideas of composition. My goal is that you can re-use the routes in nested contexts. The routing-regions has the top-level route denial handling at the moment, so it’s meant to be a “root only” node.

Eric Dvorsak 2025-09-11T09:17:20.801429Z

this was the UI before:

; NOTE: some steps are intentionally here twice, the idea is to mirror the tree of views based on the
        ;; statechart states
          (cond
            (::submitting config) (dom/div {:className "w-full h-64"} (cl/loader))
            (::error config) (error-step)

            (:state/lti-mode config)
            (cond
              (:state/lti-choosing-step config)
              (lti-choosing-step props send!)

              (:state/lti-mode-new config)
              (cond
                (:state/subject-area-step config)
                (subject-area-step props send! 1)
                (:state/interface-language-step config)
                (interface-language-step props language-course? send! 2)
                (:state/level-step config)
                (level-step props language-course? send! 3)
                (:state/set-a-timeframe-step config)
                (create-a-new-timeframe-step props send!))

              (:state/lti-mode-existing config)
              (cond
                (:state/course-choice-step config)
                (course-choice-step-ui course-choice-step
                                       {:selected-course-id (:ui/selected-course-id props)
                                        :onSelect #(send! :event/course-selected {:course-id %})
                                        :send! send!})
                (:state/set-a-course-timeframe-step config)
                (cond
                  (:state/create-a-new-timeframe-step config)
                  (create-a-new-timeframe-step props send!)
                  (:state/choose-an-existing-timeframe-step config)
                  (timeframe-choice-step-ui timeframe-choice-step
                                            {:selected-timeframe-id (:ui/selected-timeframe-id props)
                                             :onSelect #(send! :event/timeframe-selected {:timeframe-id %})
                                             :send! send!}))))

            (:state/regular-mode config)
            (cond
              (:state/name-step config)
              (name-step props send! 1)
              (:state/subject-area-step config)
              (subject-area-step props send! 2)
              (:state/interface-language-step config)
              (interface-language-step props language-course? send! 3)
              (:state/level-step config)
              (level-step props language-course? send! 4)))
with a giant chart alongside it, I'm trying to turn that into routes

tony.kay 2025-09-11T09:18:03.671529Z

oh yeah, what a mess

tony.kay 2025-09-11T09:19:02.042519Z

I’m still trying to bend my brain into how I can sub-route across an invocation boundary…

tony.kay 2025-09-11T09:19:42.527959Z

I think it’s just a matter of having event data that I can propagate into the data model of the invoked chart…

Eric Dvorsak 2025-09-11T09:20:57.422379Z

sub-route across an invocation boundary you mean being able to navigate between different routes when they are not in the same statechart?

tony.kay 2025-09-11T09:22:28.732209Z

being able to restore a route that requires invocations, possibly nested

tony.kay 2025-09-11T09:22:36.370489Z

so yes

Eric Dvorsak 2025-09-11T09:22:52.790559Z

right now I'm looking at how to navigate between my routes

tony.kay 2025-09-11T09:22:59.505869Z

main routing chart has a state for “Sales”, but then the subroutes are in an invoked chart.

Eric Dvorsak 2025-09-11T09:23:24.440089Z

uir/route-to wouldn't work since I'm not using the global uir/session-id chart

tony.kay 2025-09-11T09:23:27.298399Z

now imagine a user loading a bookmark

tony.kay 2025-09-11T09:24:09.119309Z

Are you using the uir/routes?

Eric Dvorsak 2025-09-11T09:24:19.658419Z

yes

tony.kay 2025-09-11T09:24:30.909569Z

That embeds direct transitions for every possible target

tony.kay 2025-09-11T09:24:59.829809Z

just send an event with the name (route-to-event-name Taget)

tony.kay 2025-09-11T09:26:00.250659Z

The problem is, depending on your overall chart nesting, those direct transitions may be embedded at a level where they are not visible.

tony.kay 2025-09-11T09:27:22.335819Z

I guess I need to split these things apart for proper composition to work. You should be able to use the (direct-transitions) as a node you can put at the top of your master chart. The istate needs logic to “continue” routing cross invocation boundaries.

Eric Dvorsak 2025-09-11T09:28:43.231209Z

I'm thinking in the context of a wizard I may not need uir/routes, just handling a :next event, when all the steps of a chart are done it's done, the parent moves to the next

tony.kay 2025-09-11T09:28:53.816489Z

yeah

Eric Dvorsak 2025-09-11T09:28:59.501769Z

I probably want that level of control on what the transitions should be anyway

tony.kay 2025-09-11T09:29:22.501419Z

exactly. in a general routing system, all the labeled routes are meant to be directly reachable from a bookmark

tony.kay 2025-09-11T09:29:29.035539Z

in a wizard, this is not true

Eric Dvorsak 2025-09-11T09:29:40.673439Z

and what about uir/routing-regions?

tony.kay 2025-09-11T09:30:13.242899Z

That embeds the logic for “route denied and override” handling

Eric Dvorsak 2025-09-11T09:31:33.272599Z

I can't render (uir/ui-current-subroute this comp/factory) without uir/routes though apparently

Eric Dvorsak 2025-09-11T09:32:40.966769Z

it's because it also sets route/idents

tony.kay 2025-09-11T09:32:43.262219Z

They are currently written to be paired

tony.kay 2025-09-11T09:32:58.768159Z

that’s why I said I probably need to factor them apart

tony.kay 2025-09-11T09:39:08.997979Z

I think I have a better idea for how to do route denials. Statechart semantics, if I remember right, are that “the deepest transition that matches wins”. It’s actually more complicated and well-defined than that. There’s document order and such. But, if there’s a top-level direct trasition for all things, some arbitrary sub-state can always add a

(transition {:event :route-to.*
             :cond I-am-busy?} ...)
that captures attempts to route away. Then no global handling is needed at all

tony.kay 2025-09-11T09:41:21.274569Z

Putting a wrapper around that where you could even partially allow local routing based on transition order means that is any substate taht wants to block routing you’d just end up with a bank of transitions, with your local routes listed first with their targets, and a catch-all target-less transition (with possibly a trigger of a toast or whatever) that conditionally captures other routes.

tony.kay 2025-09-11T09:42:19.061889Z

The down-side of that is that if you use it a lot it makes your chart considerably bigger…but the up-side is that it gives you unlimited localized control of how to handle being busy.

tony.kay 2025-09-11T09:43:17.899939Z

but given that I’ll have 1000 forms that all want to do it the same way, having the global as a default is probably wise…but for a wizaard, having local control of allowing partial routes is a great add-on.

Eric Dvorsak 2025-09-11T10:37:18.755459Z

send-to-self also only works with uir/session-id

Eric Dvorsak 2025-09-11T10:49:37.792719Z

I can't find a way to send-to-self

Eric Dvorsak 2025-09-12T07:27:29.989699Z

> I can't find a way to send-to-self @tony.kay colocated statecharts aren't really linked in any way to the UI component they are colocated with. send-to-self works around that by knowing the top level parent session-id and walking down from there. I think that was the biggest blocker for my teammates as well for using Invoke Basically if you Invoke your collocated statechart from a parent you can't send to the child statechart from the Child component if you don't provide a hardcoded session-id. I'm trying to list potential solutions to this problem here: without uir: • use a generated session-id known to the parent and pass it as a computed prop to the child (or even pass a send! function like use-statechart with uir • could actually do the same so as not to rely on uir/session-id statechart for isolated routing?

tony.kay 2025-09-12T07:39:13.188499Z

Ah, yeah, that is a problem…

tony.kay 2025-09-12T07:40:14.710229Z

well, but is it? This is a react hook…you’re using it locally…which are we talking about? Invocations or use-statechart?

Eric Dvorsak 2025-09-12T07:41:14.576999Z

I'm talking about invokations yes, not use-statechart

tony.kay 2025-09-12T07:42:02.471179Z

OK, so a few things: 1. UIR is not API stable. I’m about to change it significantly. 2. The use of co-located statecharts for uir/istate invocation states PRESUMES the use of UIR right now…that’s why it is in that ns. It is a UI routing invocation state.

tony.kay 2025-09-12T07:42:38.095699Z

so, in that context the routing system resolves the issue for you, but you could use the same pattern to resolve it for yourself if you want the same functionality in your own supporting code

tony.kay 2025-09-12T07:47:31.803249Z

Here’s what I realized as I was working through things. A given “routable state” almost certainly will need side-effects at times. In js these will be async. The child states MAY depend upon data established by parent states. That’s one of the issues with the current composition in Fulcro: there’s no way to be explicit about data dependencies in the routing structure that are resolved async. You can sort-of do it (route-deferred lets you defer the UI portion of the routing), but say you have this:

Event (selected by the user, contains subroutes)
  Dashboard
    Edit venue
    Edit ticket price
    ...
The sub-routes all assume that you’re already on some “event”. In Fulcro we can model this pretty easily and even pass the event details through computed props. But if some side-effect of the “active” sub-route needs that data while routing, then there’s a timing issue. Worse, side-ui concerns that might be mounted at root (e.g. a modal) won’t have the benefit of the “selected event” in the tree, leading to us creating a “selected event” data point in the database. But there’s no good place to put logic that ensures we’ve actually set selected event, or that such a process is in progress. This is actually one of the use-cases that has been driving me to move to this better model.

tony.kay 2025-09-12T07:51:40.250619Z

A statechart will ensure on-entry/on-exit, etc. BUT, the problem I didn’t consider properly was the async nature of the I/O. It isn’t a simple on-entry per route…it’s an on-entry that has to have other states to process the potentially async interactions before continuing a routing decision.

tony.kay 2025-09-12T07:52:19.199949Z

in other words I can’t apply a path “bookmark” in one shot. It has to be “consumed” by the chart and cascaded through the child states/invocations, etc.

tony.kay 2025-09-12T07:53:00.871209Z

This formalism fixes my problem, but now I’m wondering “at what cost” in terms of complexity 😄

tony.kay 2025-09-12T07:54:10.841829Z

I’m betting I can make wrappers that make it less tedious to code, but I’m not sure a visualization is goign to be very fun. So, I continue to experiment.

Eric Dvorsak 2025-09-12T08:47:30.765959Z

not sure if that is exactly the same problem you are describing but in our app we have deeplinks via urls that can go to specific entities but since they are nested in the UI eg a question is in a course in the content tab, in a particular lecture and topic (breadcrumps) so there is lots of extra attributes to grab before displaying. so what I've done is a statemachine that handles all of the general and specific concerns in terms of loading / setting up before displaying a particular entity

Eric Dvorsak 2025-09-12T08:47:43.065319Z

I assume your system intends to do that in a more generic/composable way

Eric Dvorsak 2025-09-12T08:52:28.233429Z

> so, in that context the routing system resolves the issue for you, but you could use the same pattern to resolve it for yourself if you want the same functionality in your own supporting code the pattern in uir is just to rely on a hardcoded top-level session-id AND walk down from there to find the invoked statechart related to your component right? I was hoping to figure out a more trivial solution. eg imagine i have a parent component and I want the statechart to launch a modal on some event. I would do that with an invokation, but now how woud the modal get the session-id of its own statechart if I used a generated session-id for the parent? right now I can only think of the solution I mentioned above where the session-id or send would be passed as a computed-prop to the modal

tony.kay 2025-09-12T09:06:07.581959Z

Given I don’t have a general solution to routing yet, I don’t have an answer for you. Ultimately it’s going to be “store the ID somewhere well-known, or use a constant”

tony.kay 2025-09-12T09:07:41.425269Z

An invocation NODE in a statechart can have an ID, and if supplied will be the child session id. You can also use :idlocation to have it generate a session ID and plop that into the data model of your chart. The data model IS part of app state, so you could look it up in there.

tony.kay 2025-09-12T09:08:16.662309Z

you could also base it on idents, etc.

tony.kay 2025-09-12T09:08:52.844229Z

I don’t ahve a more general solution for you with such wide-open constraints and a lack of knowledge of your own local solution…not that I’m asking to solve your larger application programming problem 😉 Other than trying (and currently failing 😄 ) to write library code for routing

tony.kay 2025-09-12T09:10:36.863489Z

I think my overall idea/approach is flawed. I actually think the right thing to do is simply provide some utilities for managing the URL from whatever statechart states and structure you want to define. Anything else makes an explosion….but I’m still trying

Eric Dvorsak 2025-09-12T09:23:04.913859Z

> Given I don’t have a general solution to routing yet, I don’t have an answer for you. Ultimately it’s going to be “store the ID somewhere well-known, or use a constant” storing the ID somewhere well-known could be a solution I think this is an important pattern to support with utilities, and maybe how uir should do send-to-self rather than the current approach if it is robus it would be good for adoption, projects can't switch their whole routing overnight, but being able to use invoke on colocated statecharts of child ui components without relying on constant session-ids turned out to be a frequent need of ours and I think many SPAs can't work with singletons (eg dahsboard and whiteboard like apps)

Eric Dvorsak 2025-09-12T14:46:22.875009Z

> you could also base it on idents, etc. as you mentioned that wouldn't work if the component doesn't have a stable ident like a form

Eric Dvorsak 2025-09-19T07:40:08.037629Z

@tony.kay I'm really with you on: > I think my overall idea/approach is flawed. I actually think the right thing to do is simply provide some utilities for managing the URL from whatever statechart states and structure you want to define. Anything else makes an explosion….but I’m still trying when I started with Fulcro RAD, I was really battling the nested form/report URL requirements because I was obsessed with having a specific format of URL with a single id in it and as little segments as possible. any form of default URL scheme seem to bring on complexity in the library and complexity in doing anything slightly different than what's expected in the implementation From you design doc > • Some level of URL integration should be possible for browser usage, but sane limitations should apply to reduce complexity. > • Complete arbitrary composition of statecharts leads to scenarios where URL representation is quite difficult or even insane. > ◦ IS possible to "fix" this problem with the user coding explcit top-level transitions that resolve conflicts. E.g. auto-detect when there is ambiguity that is not resolved by a manual top-level transition. with the right utilities and exemples even the current dynamic routing schemes should be trivial to implement by developpers to me the things I expect the most that would help do my own routing is utilities to deal with dynamic queries and connecting statecharts (at the risk of sounding like a broken clock, I'm still having issues finding how a child component can talk to its colocated statechart without relying on a singleton/known session-id)

tony.kay 2025-09-19T16:41:15.041149Z

which version of co-located charts are you talking about? use-statechart , invocations, or something else? One way to do what you want well is to probably use use-generated-id with use-statechart in the component. Then each chart has a unique ID IF the lifetime of the chart is that of the component’s on-screen time. Otherwise, something else in your app has to be responsible for the lifecycle. Making some kind of wrapper logic around that (e.g. a central statechart that does the routing as invocations and registers them somehow). IF you have a statechart like that, then the state id (of the invocation) IS a global fact, and could be used for the session id. By definition only one can be active at a time.

tony.kay 2025-09-19T16:43:56.322289Z

but use-statechart returns a send! function….so there’s that

tony.kay 2025-09-19T16:45:54.930329Z

and it looks like my istate stores the session id so you can find it:

:idlocation  [:invocation/id target-key]

tony.kay 2025-09-19T16:46:14.026019Z

where target-key is the fully-qualified classname of the target that the invocation is running

Eric Dvorsak 2025-09-20T09:44:38.309949Z

but the solutions you are describing imply that if I want to send to the colocated statechart from within the child, I have to write code that is aware of that global fact. I'm thinking that when invoking the colocated statechart of a component, if the component has a statechart, a invoking util such as istate should store the statechart ident under the key :ui/statechart at the component ident. that way even if the component changes ident like a form that saves for the first time the :ui/statechart will still be there. that way the child can send events to it's colocated statechart without knowing which global statechart it depends on.

tony.kay 2025-09-22T07:29:23.926819Z

So, ARE you using ui routing or not? If you ARE, then you already have the solution written for you in uir/send-to-self! , but I’m likely to deprecate the entire namespace in light of developments. If you want to make your own istate that works with dynamic queries on a component to support some level of “routing” and want to co-locate the session ID on the state of the component, that sounds perfectly sane. I’m quite busy recently, so I don’t have time to do that for you, nor review it for including in the library at the moment, but all of the hard work is done, and you have examples of how to write any of those elements in the existing code.

tony.kay 2025-09-22T07:30:13.395479Z

The statecharts library was specifically designed to make such extensions trivial to write, as you’ve seen, with the intention that you’ll invent what works best for you anyway

Eric Dvorsak 2025-09-22T14:10:56.336399Z

I've experimented with the ui routing locally but send-to-self! wasn't working for me since I didn't use the global statechart. But yeah I have a good idea on how to do it, I'm in no hurry ATM as I still use the dynamic routing and use-statechart.

tony.kay 2025-09-11T13:29:28.724249Z

I’m realizing now, after all of this design work, that the overall model is too simple to support what I want as a final result. So, the entire overall design needs to change. I’ll be updating the design doc more as I put together a prototype of the new approach.

tony.kay 2025-09-15T08:50:13.991109Z

I've been doing some hammock time when I laid out an actual fully functional asynchronous solution, it was rather complex. I'm starting to feel like the basic primitive I should give is something that allows you to easily change the dynamic query of a component, and to do an invocation around that, and for your component to handle everything else from there. The problem with compositional routing is that the asynchronous behavior of a parent route may want to block a child route. In other words, you may have data dependencies from the parent that need to resolve before you continue on to the child's State. State charts don't have this mechanism built into States themselves, you have to build a chart that describes these interactions. Interactions. So, generalizing that makes kind of a mess because each subrout gets its own complete combination of States and entry and exit handlers and events and transitions etc

tony.kay 2025-09-15T08:51:30.529469Z

Url integration is a major pain, unless you just assign particular URLs to a particular final state, and then the synchronization system at least from inside out can easily work. Re-establishing, a particular State on an incoming URL is kind of the big difficult problem

tony.kay 2025-09-15T08:52:52.095329Z

Because transitioning to some extremely nested state where there were i o dependencies that were asynchronous in nature, and where you probably had to invocations and such, means such transitions are rather complex.

tony.kay 2025-09-15T08:54:34.191109Z

I'm starting to lean towards making a state chart-based version of dynamic routing, and keeping the compositional hooks exactly the same, but just cleaning up the internal logic using state charts. This would separate the entire routing issue into its own concern, which now that I've looked at it, was probably the right design choice to begin with. I could also just try to fix up the existing issues and dynamic routing, and leave it in UI State machines. The only reason to rewrite it, would be to then get the visualization support that is already partially extant in my state charts support in inspect.

Eric Dvorsak 2025-09-15T08:54:38.342339Z

honestly I like the id of "just assign particular URLs to a particular final state"

tony.kay 2025-09-15T08:55:22.734739Z

Yeah, but there's no clean way to transition to that Target State. Unless you provide the input URL transition yourself

Eric Dvorsak 2025-09-15T08:56:13.120219Z

> no clean way to transition to that Target State. Unless you provide the input URL, transition yourself I don't get this

tony.kay 2025-09-15T08:56:45.528759Z

Imagine a parent state that needs to load the current context before it can allow a transition to a substate. So, if a URL is placed on that substate, how does the routing system get through the upper States that need to run and transition and do all their handling to do the loading?

tony.kay 2025-09-15T08:57:24.031809Z

The dynamic routing system as it currently exists gives you a UI State machine for each router. And so cascading through the different routers to each subtarget allows i o control at each layer

tony.kay 2025-09-15T08:58:10.150409Z

State charts don't have asynchronous transitions. Neither do uI state machines. You represent asynchrony through events and various states that model the exact sequence, error handling, etc

Eric Dvorsak 2025-09-15T09:02:55.757179Z

I would expect that the URL contains enough parameters to reach the state where it is defined. that is actually how I do it in my current-course state machine right now the url to a question only contains a question-id so the state machine gathers additional params to reach the full routing state before routing, like the course-id, topic-id etc... from the API essentially letting pathom figure out how to get them

tony.kay 2025-09-15T09:18:20.447779Z

Yes, in the trivial case that's fine. But if you're trying to design a general reusable system for arbitrary systems, then you have to consider that there might be asynchronous dependencies. Your simplified scheme simply doesn't support that. You'd have to spell out the rest of the chart, and then an external system. Trying to enforce an incoming URL would not be able to do it in a single synchronous step, other than to send you an event saying hey, this URL needs to be displayed

tony.kay 2025-09-15T09:19:14.815779Z

At which point, the routing system itself isn't a solution. See what I mean?

Eric Dvorsak 2025-09-15T06:43:31.599989Z

this week end I was thinking about making an abstraction for our pattern of having tabs in modals, but then I realized that it falls once again under the umbrella of a routing solution, opening a modal is just a parallel state, each tab is a substate.