Fork me on GitHub
#fulcro
<
2019-11-07
>
henrik11:11:38

How do you query global things like the current user from arbitrary components? How do you pass it through a router? Should I make the router target an ident-less component, which queries the current user, and in turn passes the current user as computed to the ident-ful component?

mdhaney17:11:09

What you’re looking for is a link query: http://book.fulcrologic.com/fulcro3/#_link_queries

👍 4
henrik11:11:45

No, hang on, the router requires idents on its subcomponents.

henrik11:11:20

Aha,

(fc/make-fulcro-client
  {:reconciler-options {:shared {:pi 3.14} ; never changes
                        :shared-fn #(select-keys % :current-user)}}) ; updates on root render

henrik11:11:43

(`#(select-keys % :current-user)` should probably be #(select-keys % [:current-user]))

henrik12:11:49

A question regarding root re-renders then. Since I want this information available to the login form (in order to fashion a multi-step login, where entering an unknown user id/email leads to account creation, a la Slack etc.), I seem to run into race conditions between the shared function, root re-render, and the data entered into the login form fields. I.e., User is logged in and clicks “log out” -> root apparently rerenders before the session is terminated in the DB, passing the previously logged in user’s details to the login form. When rendering the login form, the values are set to {:session/current-user {:user/id #uuid "40a9046a-7130-4951-bda8-28664fd77d07"… even though the user is logged out as far as the app state is concerned). If I force a rerender at this stage, they are set to nil etc. as it should. Would it be idiomatic to force a root rerender upon mounting the login form to ensure up-to-date shared data?

johnny14:11:42

Personally I would re-render after a successfull login / logout. Your login form should in my opinion just query the current-user, rather than using the current-user from it's shared state. This way you make sure it's up-to-date.

henrik14:11:13

Right, but the current user is kind of a global, root-level thing. Can I arbitrarily query root-level things in in subcomponents?

henrik14:11:21

(The app does rerender on login/logout already, but the shared-state updates seems to lag somewhat behind that re-render)

cjmurphy16:11:52

You can have keys that are right at the root of app state, and query for them from any component. Especially useful for things like the 'current user'. See http://book.fulcrologic.com/fulcro3/#_link_queries.

👍 4
henrik17:11:53

Thanks, I’ve read the documentation back to front, but managed to skip over that one somehow.

tony.kay17:11:15

I would recommend link queries over shared. Shared has been around since the beginning, but was never my favorite feature…but removing it is impossible since ppl have come to rely on it. There is also, of course, react context, top-level atoms, etc. Lots of ways to accomplish similar goals, but link queries participate in the rendering refresh story, so they are more reliable.

henrik17:11:06

I will throw it out with haste!

henrik17:11:46

I sat down with the UI state machine video and converted the session code, and that cleared quite a few things up. And right at the end, you briefly demonstrate the '_ syntax as well, which would have answered my question if I hadn’t gone ahead of myself.

henrik18:11:02

Querying in the root for the equivalent of [::uism/asm-id ::session/sessions] results in a Failed to do optimized update… queries for data that changed, but does not have an ident. Removing it seems to restore order. Should I just ignore this?

tony.kay18:11:32

That warning is telling you that a component that needed an update did not have an ident, and it was forced to render from root

tony.kay18:11:45

I’m guessing you added that to a query of a component without an idnet?

tony.kay18:11:21

and you should not really use root for much at all other than a thin wrapper if you’re doing that in root

tony.kay18:11:48

if root component is what gave you the warning then it can be ignored, but I still recommend moving real logic out of root

henrik18:11:11

There are three queries, an FSM, the router, and CurrentUser, which has an ident of (fn [] [::session :current-user])

tony.kay18:11:27

I’ll update the warning..it should not issue if it is root

henrik18:11:55

It’s not CurrentUser, so it’s the UISM it’s reacting to I’m guessing (the router seems unlikely). Would it be a good idea to wrap the session UISM readiness stuff and router in a subcomponent?

tony.kay18:11:55

anything you query for in the root will trigger root re-renders when that data changes…

henrik18:11:15

Given the global nature of session stuff, I guess this is OK for this particular use case.

tony.kay18:11:35

same with things like i18n locale

tony.kay18:11:41

you want a full refresh

tony.kay18:11:03

would you mind testing the warning for me?

henrik18:11:24

Of course not

tony.kay18:11:26

3.0.7-SNAPSHOT has a fix

👍 4
tony.kay18:11:41

should no longer warn if the change is in the root itself

tony.kay18:11:16

that version also optimizes for space…and it has not been tested yet for that…so let me know if anything fishy happens

henrik18:11:58

Sure, I love living on the edge. It’s currently running against the master branch of ClojureScript as well. All is well on that front.

henrik18:11:55

So, it’s giving the shorter message of Optimized render failed. Falling back to root render.

timovanderkamp18:11:47

I just read in the book that there are different ways of rendering optimizations, how do you alternate between them?

timovanderkamp19:11:32

Nevermind i found it, its the optimized-render! key in a fulcro app

`:optimized-render!` - A function that can analyze the state of the application and optimally refresh the screen.
     Defaults to `ident-optimized-render/render!`, but can also be set to `keyframe-render/render!`.  Further customizations are
     also possible.  Most applications will likely be best with the default (which analyzes changes by ident and targets
     refreshes), but applications with a lot of on-screen components may find the keyframe renderer to be faster. Both
     get added benefits from Fulcro's default `shouldComponentUpdate`, which will prevent rendering when there are no real
     changes.

timovanderkamp19:11:12

Keyframe-render is all I want in life

✔️ 4
henrik19:11:18

I’ve found the ident renderer quite accommodating so far.

tony.kay19:11:31

I spent a lot of time making the ident-based optimizations good, but they do still require you to indicate refresh on dependent data (parent looks into children to render things like summaries)…the keyframe makes everything magically automatic, but it does sacrifice a bit of performance. That said, keyframe on most desktop computers will be fast enough…mobile is where you might reconsider. Even then, using React-style optimizations (use component-local state to do form input control) is usually about all it takes to make it all irrelevant, if somewhat more buggy and complex 😜

timovanderkamp20:11:28

I was rewriting from fulcro 2 to 3 and realised how many follow-on-reads we used in txes.

henrik19:11:16

Speaking of renderers and idents, if you have a component hierarchy like A -> B -> C, and a mutation called from C needs the complete view as A sees it, is passing information down through computed from A via B to C the correct way to go about it? Or is there some way of inspecting the component hierarchy from the mutation called by C?

💻 4
cjmurphy19:11:35

@henrik A mutation has the whole of app state to play with, so a mutation called at C will see all state from all other components. The only thing is that the data you are inspecting will be normalised, so your code will have to follow idents etc.

henrik19:11:36

Yes, but it doesn’t necessarily know which B or A it belongs to unless I give it that information, does it?

henrik19:11:10

I.e., B knows which Cs it owns, but a C doesn’t know anything about the calling B necessarily.

cjmurphy19:11:07

You can check if particular ident is in a to-many.

cjmurphy19:11:41

or a to-one.

henrik19:11:42

Right, filter on all Bs, then filter on all As.

cjmurphy19:11:30

Yes, the layout of your screen should be there in the app state.

cjmurphy19:11:27

But you still might want to pass helpful things down as you say.

cjmurphy19:11:09

And for component to component communication (as I call it) you use computed.

henrik19:11:56

Yeah, it wouldn’t help with dragging and dropping a C from one B to another I think, since the mutation doesn’t happen until after the drop, at which point it needs to know both the target and source B to move the C.

cjmurphy19:11:35

So id of A being passed to B and of B to C might make sense. But none of this should be essential, as your app state tells the whole story.

henrik19:11:59

No, hang on. You’re right, the source can still be derived from the app state, and the target is implicit in where it is dropped.

henrik19:11:44

Thanks for the advice, I’ve gone a bit overboard with the computed in the implementation. I think I’m going to see how much I can derive from the app state and confine to the mutations.

cjmurphy19:11:24

Yes, you have the luxury of two events if you are talking drag and drop, so two mutations, so chance to put anything helpful you might need into state as well.

cjmurphy19:11:11

Yes - becomes all about mutations, and st->st* functions they call...