Fork me on GitHub
#fulcro
<
2019-12-18
>
tony.kay02:12:56

Fulcro 3.0.17 on clojars, which supports setting the option you need

twicebaked14:12:42

Is it an anti-pattern to have dummy/shim components to use to watch/query for changes in the app DB? In other frameworks I've used things that amount to watches on atoms, but doing that directly on the state atom feels wrong in Fulcro. I can explain further but it's a bit of a wall of text. Generally what I'm trying to do is sync a complex state graph in the app DB with a 3rd party JS only component that can only be directly wrapped by a single Fulcro component.

tony.kay14:12:51

@twicebaked The standard pattern for dealing with adding data that will refresh things is to use merge-component! and let Fulcro figure it out from there. So if you mean “is it ok for a component to have a query on data it passes to some child that is js?“, yes.

tony.kay14:12:08

“watch” isn’t the term I’d use, since that brings up connotations of atoms and event systems and such

twicebaked14:12:22

well by watch I did mean the state atom

twicebaked14:12:35

Perhaps a concrete example will help more

tony.kay14:12:39

no, that is what the component queries are for: to ask for what they need out of the db

tony.kay14:12:55

then they will re-render and can refresh children…which is the wrapping I think you meant

twicebaked14:12:45

The actual problem is this - the 3rd party component is a JS-only editor with a very large API. The editor renders a lot of UI and buries its own state internally. The actual API can only be called after it attaches itself to the DOM, meaning only after my Fulcro component attaches it in componentDidMount . What I'd like to do for instance is to make a change to some data related to the editor and have it call the editor's API.

twicebaked14:12:42

A good example would be I have some metadata that is used to calculate text decorations. The only way to visually update the editor is through its API. When the source data gets changed in the APP DB, I want it to automatically call the editor's API.

twicebaked15:12:51

The wrapping Fulcro editor component itself just queries an "editor" table at the top-level with some relevant state. Updating that stuff is easy, ex: via shouldComponentUpdate. Where it becomes more annoying is when the state I need is a few relationships away, but there's no real on-screen representation other than in the editor itself (visually, and the state itself is hidden away in its internal OO state).

twicebaked15:12:54

Right now I'm using mostly a combination of the editor's event hooks, pre-merge, react lifecycle methods, mutations, and post-mutations (on load) to manage the mess. The wrapping component though is turning into a bit of a monster so my hope was to perhaps farm out managing some of the state changes somehow.

twicebaked15:12:33

The answer might be there's no way around it 😄

tony.kay15:12:01

so, any time something manages DOM of its own you’re in a shouldComponentUpdate/componentDidUpdate sort of situation

twicebaked15:12:34

yeah that's what I am doing now and it works, but it's certainly a pain when dealing with things that aren't for instance in the editor table itself

tony.kay15:12:06

so, your problem is this editor has something you need…what are you thinking you could do there?

tony.kay15:12:17

I mean, does it have an OO event API or something?

twicebaked15:12:19

So far everything I am doing works, my goal is to really simplify

tony.kay15:12:29

I think it is an anti-pattern to link to anything that says microsoft…

twicebaked15:12:38

it feels like I just have this giant mess hidden by functions

twicebaked15:12:54

I'd gladly change if not for a few reasons 1. out of my control 2. It at least has an underlying buffer that isn't just some ugly line array that ends up crapping itself when the content grows or for certain hot ops 3. Wide community support at least

tony.kay15:12:57

“Where it becomes more annoying is when the state I need is a few relationships away”…you mean in app db? You are aware of link queries, right?

twicebaked15:12:10

yeah I'm aware of link queries

twicebaked15:12:20

even on my side, the state I need to sync with it can get pretty deep, especially because there's no way around some of the design of the editor itself, ex: some things need to be done to the editor, some to the underlying buffer (also a specialized object)

tony.kay15:12:30

So, you’re certainly allowed to watch Fulcro’s state atom. The query system is there to make that less obnoxious and to handle efficient refresh. If you watch the state atom and do side-effecting sorts of things into you js component, that is fine. Not going to hurt Fulcro

twicebaked15:12:06

Yeah that's where I thought what if I did something stupid like create components that query some of these other tables and make the API calls in shouldComponentUpdate for example

twicebaked15:12:39

then I could at least group them in some component that includes the editor and all these other supporting components that don't really have an on-screen representation themselves

tony.kay15:12:24

Yes, there’s no getting around some of that kind of ugliness when the thing you are working with requires it

twicebaked15:12:52

OK that's good to know at least there isn't some kind of other pattern or feature I missed that would simplify things

tony.kay15:12:02

I’d prefer a clear normalized data model in app state as a control, and the lifecycle methods as the (hidden) ugliness that makes it work

tony.kay15:12:11

no watch on atom

twicebaked15:12:23

yeah hence the components instead of a watch

tony.kay15:12:03

For example, I’ve written a 3d app that uses three.js…Fulcro does very little in the wrapper, and actually hooks up a separate 3d reconciler for the internal state of three

tony.kay15:12:42

but it reconciles my normalized objects from app state into the mutable objects in three

tony.kay15:12:30

in that case I’m asking for the entire table [:object/id '_] in my query

tony.kay15:12:39

and passing that table to my 3d reconciler

tony.kay15:12:51

that way Fulcro’s overhead is minimized (doesn’t have to do denormalization on the objects, which are self-contained anyway)

twicebaked15:12:20

OK that makes sense, so one query grabs the entire state tree and that deals with it elsewhere

tony.kay15:12:40

and you leverage just enough normalization in Fulcro to get sanity in your data

twicebaked15:12:42

I do have a good amount of state that is read/generated elsewhere so that part needs to still be normalized by Fulcro probably

tony.kay15:12:09

and that’s where you can choose to use components that themselves do no render, but are used in query of components for data merge story

twicebaked15:12:51

for the load part I do that already

tony.kay15:12:59

Components that correspond to normalized data must be represented in the query tree of the UI components, but the UI components only have to use them for rendering if they care about targeted refresh of the children

twicebaked15:12:12

if I had the time I'd just write my own editor in Fulcro

tony.kay15:12:25

that’s always the trouble 🙂

twicebaked15:12:37

comparing some of the things I'm doing to similar code in vscode, it's already ridiculous how much they overcomplicate it and how simple those things look in fulcro

tony.kay15:12:38

it’s best to leverage community stuff for things like this

tony.kay15:12:08

but yes, I have been working on Fulcro for 3 years and it still surprises me how much it cleans things up 😉

twicebaked15:12:39

I wrote an initial prototype of this project in typescript

twicebaked15:12:50

I wanted to die because the whole time I just wanted an app db

twicebaked15:12:04

convinced them to let me do some things in Fulcro since it simplified everything else

twicebaked15:12:15

I swear it's orders of magnitude less code which is not shocking at all

twicebaked15:12:47

anyway thanks for the input, I'll let you get back to it. Much appreciated.

tony.kay15:12:42

3.0.18 on clojars. Fixes a regression in inspect reporting (UISM is not showing events, but instead the low-level mutation call)

👍 8
mruzekw15:12:03

Are Fulcro state machines hierarchical?

mruzekw15:12:11

Or can they be?

tony.kay15:12:03

not really. My intention was to get something working with the amount of time I had to implement it. I read Harel’s paper on state charts, and decided that was more than I wanted to tackle, esp since I could get what I wanted from simple FSM. After using them I realized that Fulcro already gives you normalized state, where you can track/have any number of state machines. This gives you the essentials to get “history” when you re-enter (i.e. show the UI for) a given machine. They can send events to each other, so that gives you general communication.

tony.kay15:12:42

heirarchy can easily be structured from this by simply passing the IDs of state machines to each other

mruzekw15:12:06

> They can send events to each other Like actors?

tony.kay15:12:11

in fact, that whole mess can be dynamic, which i’m leveraging in RAD: the master controller is sent IDs of state machines it will communicate to

tony.kay15:12:28

careful with terminology…I unfortunately overloaded the concept of actors in UISM in Fulcro

tony.kay15:12:48

but yes, similar to actors in dist system terms

mruzekw15:12:14

Okay, thanks. So it sounds like it’s possible through means of IDs

tony.kay15:12:15

in UISM, an actor is a UI actor…a component filling a role, so you can swap out UI instances on a running state machine, or re-use logic with diff UI impl

tony.kay15:12:16

it actually turned out to be super flexible…yes, I think you’ll be able to get what you want, though with slightly different approaches. The down-side is I’ve not thought through the full semantics of things like Harel did…so the model is not as theoretically sound.

tony.kay15:12:30

but it is working very well for me 🙂

mruzekw15:12:49

I see. Alright. I’m still a Fulcro noob, so for all I know statecharts are not needed.

mruzekw15:12:13

Just saw that going around in the JS world with XState and wondered if that were the implementation here

tony.kay15:12:39

no, xstate is true state charts

tony.kay15:12:24

UISM is FSM tightly integrated with Fulcro primitives, and because there is a state db already you end up with some of the things statecharts is giving you. By putting the state machines in Fulcro’s app db you end up with interesting additions to FSM that mimic the abilities you get with S.C. You have “instances”, can have more than one (with known ID), can communicate between them (triggering events across machines)….that latter bit is what ends up giving you a lot of the xstate stuff…it just isn’t hierarchical…so you lose something there.

tony.kay15:12:13

the “outer” vs “inner” scope isn’t implemented for you…though if you want that, you can technically have the “inner” one read the details of the “outer” one using the state map and the outer one’s ID.

tony.kay15:12:27

it’s a global normalized db

mruzekw15:12:25

Okay, makes sense. Thanks.

mruzekw15:12:44

Is UISM pluggable? Could one write an SC implementation and replace the current one?

tony.kay15:12:07

no need for it to be…I’d suggest just a separate ns that implements it…there is zero coupling

tony.kay15:12:56

you could probably wrap the current API with another and get it…but I’d suggest you implement some things first to see if you actually notice a failing that inspires the extra work

tony.kay15:12:07

so far, I have not

mruzekw15:12:09

The holy grail 🍷

mruzekw15:12:22

zero coupling ^

tony.kay15:12:47

well…it’s unidirectional…UISM does have to understand how Fulcro normalizes things

tony.kay15:12:03

but Fulcro has zero knowledge of UISM

tony.kay15:12:59

it was originally in the incubator library as an optional extension, but was so useful in our projects that I made it official ns in F3

👍 4
mruzekw15:12:26

Nice, well I’ll give UISM a try before writing anything

mruzekw15:12:03

Appreciate your explanations.

tony.kay20:12:25

Warning: Fulcro 3.0.14+ will break custom http network middleware. In fact, there is no way for me to fix the thing that was broken without an incompatible change at that layer. It was an oversight in much earlier design phases (pre-3.0). I’m going to bump to 3.1.0 shortly, which really should have been done at 3.0.14. Sorry about that. I’ll document the fix in the middleware docs of the devel guide when I get it done.

tony.kay21:12:58

I’ve released 3.1.0 to clojars. This should not be a breaking change for most people, but the interface at the http-remote client response middleware had to change slightly. The response map no longer contains a :transaction key from the raw http remote, it instead contains and :original-transaction. Middlware that wants to rewrite the transaction MUST return :transaction in the responss still, and therefore response middleware should check for both keys.