Fork me on GitHub

@mdhaney Wow,Thank you for your reply.I can’t figure out why my :started-callback can’t load things from the server.How do you debug the client server communication problem?Thanks.😊


console.error: "[18.362s]  [] Transit decode failed! SyntaxError:Unexpected token u in JSON at position 0"


Really hope to see your demo projects or project template.


@eric.shao what are you sending back and forth? That is a transit failure. Perhaps you sent some data that transit can encode in JVM, but not decode in cljs?


@tony.kay I think it is in cljs,not even touch the server. 1. The df/load code in RN client side is identical to the Web client side .And the web’s reaction is right. 2: The problem is in df/load(there is a console.log) but before the http request was even sent. Because in the react native debug console’s network tab there is not a new XHR and the console.log in server’s defquery is not invoked. I’ll try again from scratch.


1. Still the same Transit decode error. 2. The :ident begin to show problem [ 3.697s] [fulcro.client.primitives] component main.state-machine-ws.ui.components/PersonList’s ident ([:person-list/by-id nil]) has a nil second element. This warning can be safely ignored if that is intended. [ 3.703s] [fulcro.client.primitives] component main.state-machine-ws.ui.components/Person’s ident ([:person/by-id nil]) has a nil second element. This warning can be safely ignored if that is intended. 3. Without REPL and FulcroInspector it’s hard to http://program.It reminds me how much work tony has put to make our work easy with FulcroWeb. Thank you Mr. tony.kay. 4. Mr @mdhaney , please share your experience in code and articles.😊


Hi! I have a “library” for my Fulcro project in the main directory, i.e. src/main/my_project/core/lib.cljc. I would like to test it using devcards, so I created a file src/cards/my_project/core/lib.cljs (mirroring the main tree). I thought I would be apple to access methods define in my lib.cljc file but it looks like it’s not getting compiled as part of shadow-cljs watch cards. How can I remedy this?


I solved the problem by renaming my cards namespace to lib_cards, after which I could import the core/lib.cljc namespace and take it from there. I would rather have both share the same namespace though, so my question still stands 🙂


problem solved no #shadow-cljs


@eric.shao your problem might be shadow-cljs. When I started this project and looked into shadow-cljs, they were in limbo with RN support (there were some older attempts that kinda worked, but then broke with newer releases). The Expo lein template worked out of the box, but it doesn’t support Fulcro and only supported Expo 26, which is about 10 months old now. What I did was start with the Expo template and, then swapped out Fulcro for Om and upgraded the Expo version to 30, which is the last release on RN 55 (versions 56 and 57 had a lot of tooling changes, and last I checked re-natal wasn’t even supporting them yet). This has worked quite well. I get a working REPL, which has proved invaluable. Fulcro inspect isn’t working - it’s not detecting the Fulcro app when I run the app remotely on Chrome. It seems to me this might be fixable, but I haven’t had the cycles to dig into it. To avoid the externs issue, I’m using cljs-oops and so far that has worked well. I’ll pull out the core client code and upload a project tonight. Converting the Expo template to use Fulcro and upgrade Expo was pretty straightforward, but IIRC there were a couple of gotchas during the process.


Fulcro 2.7.0 released. This includes a breaking change in websockets (the API for sente changed due to a security hole (which we had a workaround for)). It also fixes a bug in SSR state encode/decode. Thanks @lennart.buit.

👍 13

@tony.kay I am about to start a new fulcro project. Would you recommend the websocket transport or the HTTP transport? My only real requirement is that order of mutations is preserved (i.e. the transport doesnt submit a mutation to the server until it has received a confirmation by the server that the previous mutation has been processed)


@hmaurer both are fine. If you submit a tx as a unit to a single remote, then the sequence of calls in that tx will be preserved, but it will be transmitted all at once. So, the behavior on the server is up to you. If the mutations are queued in separate event invocations, then Fulcro, by default, serializes requests


I.e. [(f)] [(g)] is possibly different in processing from [(f) (g)], but is up to your implementation on the server. There are 2 possible scenarios for the 1st.


If 2 txes are submitted on the same user event, then they are combined, and are equivalent to the second form


Okay, so both would be fine? My use-case would be the later (separate mutations). Roughly speaking I am building an exercise application, where the state of exercises are dictated by a sequence of events. The user (student) does an action on the frontend which moidfies the state of the exercise. I want to apply it optimistically on the frontend (to show new state without lag) and also transmit the event to the backend to persist the student’s current exercise state


unless they share mutation name. [(f) (f)] is always split into 2


The reason why it needs serialisation is that a student might perform multiple actions quickly in a row, in which case we would want the server state to be the same as the client’s state


exercise state I mean


yep…that is one very important thing that was designed in from the beginning


Great 🙂 And what is the default behaviour in case of failure on the server side? (also, is the default behaviour to apply the mutation optimistically on the frontend?)


See pessimistic mutation support in Fulcro Incubator


The book goes through all of this, but the end result it you probably want that


it allows for optimistic UI update and easy failure handling


The optimistic update is not rolled back for you, if that is what you’re asking.


Just too many possible desired behaviors (you might want to retry instead)


so there is no automation around error handling


Yep, I was basically asking: if A -> B -> C are applied optimistically on the frontend (where A, B, C are mutations), and the server queue fails on B, what happens?


but if the answer is in the book I won’t bother you further


the answer is “that depends”


Start by reading the stuff on ptransact! in the book, then look at the pessimistic mutation docs in incubator


is a good starting point


and (late) merry christmas 🙂


Thanks, happy holidays to you as well 🙂


I’ll definitely read that; in my case I don’t want to hard fail if a mutation fails. The idea is to “sync” a client state (exercise state) with a server state. So I want to let the student keep going in the exercise, and perhaps if syncing really fails (i.e. 3 retries fail) then give a warning that the connexion might be broken and that his progress will be lost


or syncing might fail because the auth token expired, in which case I would just want to refresh it and retry


Hm…the docs on incubator are a little weird on that. You can use normal Fulcro ptransact! with those now…it mentions transact functions in incubator, which are not necessary.


those are the only two scenarios I can think of


So, you might even consider implementing retry at the network layer. The built-in Fulcro remote isn’t that complicated (use the source), and that kind of recovery would probably be best handled at that layer.


Websocket support has a basic version of that (auto-retry), BUT it requires your mutations be idempotent (as your own implementation would need to as well, since you can’t know on a failure what the server saw)


does that suggestion apply regardless of whether the transport is http or websocket? or is one easier than the other ?


ah, you tackled that point faster than I wrote my question 😛


I guess I can make mutations idempotent by tagging them with an ID and check that on the backend


in my case that’s an acceptable solution


it won’t have noticeable performance impact


Auto-retry could be added to the built in normal one as well, but most of my projects use websockets, so it got the attn on that.


so I guess using websocket is also a better idea if I want to get the most out of you, should any issue arise 😛


not necessarily 🙂 The code works “well enough” for me that I’ve not had to look at the network layer in some time. I wouldn’t say I’m any better at dealing with issues on either.


I looked further at the docs. It think the incubator stuff is actually all ok…the comment on “composition” is what I was trying to get at/remember.


so it is probable you sshould just trust the docs 🙂


Yeah…the docs are right


But I think they might mislead you slightly unless you know this: Separate invocations will not have a blocking behavior (e.g. 2 calls to ptransact will never be combined of block each other)…so, it requires a bit more care…but auto-retry at the network layer sounds like a better call. YOu can also get network status notifications from websockets (though they are sometimes delayed a lot by browser implementation).


E.g. (pm/ptransact! this [(f) (g)]) may delay the invocation of g until f completes full stack…but if the user submits a new tx (h), then that one won’t* necessarily be blocked by the completion of the (yet incomplete) g. You could see f, h, g. The order that is guaranteed is that g comes after f. period.


Sorry, just to clarify: (prim/ptransact! this '[(a) (b)]) will run (a) and (b) serially, but you said that mutations are also queued by remote, right? and serially applied


so if h got queued while f was running, then it may run before g


The queue of g doesn’t even happen until f completes


ah, so both f and g don’t get queued at once, then h queued after that?


and if I wanted to run those 3 actions serially?


f, g then h


(but I don’t want to add h to the same ptransact call)


have to be in same tx, or in 3 diff ones


the former uses the pessimistic support, the latter relies on the queue


but the latter does not let you short-circuit



(pm/ptransact! this [(f)])
(pm/ptransact! this [(g)])
then later
(pm/ptransact! this [(h)])


those will go in order, BUT all of the optimistic will run immediately


the network queue is just about remotes


optimistic is always immediate unless using ptransact and a single tx


whereas with (pm/ptransact! this [(f) (g)]) it would optimistically run f, wait for completion, and if successful optimistically run g and wait for comlpetion?


in which case full-stack has to complete before next runs optimistically


Thanks for pointing that out; it would definitely have confused me if I stumbled on it


is there one queue per remote btw?


The main point of ptransact is to be able to hold off on optimistic updates until a remote has completed.


one queue per, yes


ptransact works across them


BUT, do not turn off serial operation on the remote (which is an option). That will break it (it bypasses the queue)