Fork me on GitHub
#fulcro
<
2018-06-26
>
claudiu13:06:23

@tony.kay Are there any plans to add a graphql network layer in fulcro ? 🙂 Thinking of people that would like to have something like Apollo client in clojurescript.

wilkerlucio13:06:49

@claudiu you can use pathom for that 🙂

wilkerlucio13:06:30

the documentation still missing, but it has support for GraphQL integration

claudiu13:06:37

@wilkerlucio Yep. Was just looking at that 🙂 But was thinking having that extra step (no mention in the fulcro docs) might make a lot of people view fulcro as only a "full-stack" library, and not a prime-candidate if they already have a graphql backend and just want to use fulcro on the client side.

wilkerlucio13:06:42

yeah, it's mostly a documentation problem at this point, but pathom still need some extra work to make it easier to use, the current setup is cumbersome, but I'm working on some things to make that easier

wilkerlucio13:06:00

I just have been very very busy lately, but I'll get there 🙂

wilkerlucio13:06:42

will help a lot when pathom provides some http drivers, setting those is the major pain point now

claudiu13:06:19

sounds really cool. Haven't tried it, but looking at the EDN→GraphQL seems like there is a good chance that it could work really nice with graphql on the backend, without to many compromises.

wilkerlucio14:06:01

yeah, I had to remove from the docs because they got outdated, but there was an example on how to connect directly with the Github GraphQL API, and also how to mix that with other graphql endpoints or rest, you can mix and match everything there

tony.kay15:06:08

@claudiu Pathom is the official answer for Fulcro in all things query processing/parsing

claudiu16:06:01

Somebody mentioned that fulcro seems to full-stack and not a good fit for current projects. The docs cover restfull api, but could not find anything to share about fulcro + graphql.

claudiu16:06:00

Was thinking about apollo, and that fulcro is pretty much the closest thing in clojurescript. If it would work out of the box with graphql that would be pretty amazing.

tony.kay17:06:34

We’re working towards that, but Wilker and I are both pretty busy at the moment. I understand that it is a critical use-case.

tony.kay17:06:20

probably the most critical…but also somewhat time consuming in terms of documentation….which as Wilker says, is the majority of the remaining work.

currentoor17:06:45

@claudiu FWIW fulcro has a backend story, but it's not required at all, you can implement your own remote on the frontend and make it work with whatever you want

currentoor17:06:18

i used fulcro with an existing rails backend and it worked well

tony.kay17:06:45

I think his point is more that people are looking for GraphQL, and we can do it well, but there is no doc/example

claudiu05:06:46

@tony.kay Yep. Referring to tweets like this https://twitter.com/lilactown_/status/995132059734556672 . Can see how if you have an existing graphql backend could be easy to miss the point that fulcro can do it really well.

claudiu05:06:03

Started rebuilding my personal project in fulcro, but the more I think about it it would make more sense to have the backend in graphql than fulcro server. No complaints about datomic pull syntax for the workflow... But graphql is the "standard", and I could expose that to clients also, plenty of docs & big websites that have graphql public api.... Doubt any third party clients would bother learning & using my edn public api.

tony.kay17:06:02

which I agree with…it is a hole that needs filled ASAP

currentoor17:06:24

yeah does seem like of all the cljs react frameworks, fulcro is in the best position to take advantage of GraphQL

Chris Swanson17:06:29

i'm having more confusion about parent components not getting re-rendered, even though I'm using keyframe mode now

Chris Swanson17:06:40

i've got this router:

Chris Swanson17:06:58

(defrouter ProfileShowRouter :profile-show-router
  (fn [this props] [:profile/by-id (:profile/id props)])
  :profile/by-id Profile)

Chris Swanson17:06:41

and a mutation that triggers it to change:

(m/defmutation show-profile-id [{:keys [profile-id]}]
  (action [{:keys [state] :as env}]
          (swap! state (fn [s]
                         (-> s
                             (fs/add-form-config* Profile [:profile/by-id profile-id]))))
          (r/route-to-impl! env {:handler :profile-show
                                 :route-params {:profile-id profile-id}})))

Chris Swanson17:06:39

There is a child component of Profile that is supposed to be about to trigger a route to another profile (ie profiles can have links to one another)

Chris Swanson17:06:26

When I click the link, I can see in the database that the router updates to the correct profile id. And the fields passed down to the child component that triggered the mutation get updated.

Chris Swanson17:06:55

But the fields on the Profile component itself don't change, as if the query didn't get rerun when the route happened

Chris Swanson17:06:39

I'm not sure what I'm missing, why the router changing wouldn't trigger a refresh of all its child components , since the id of the profile has changed

Chris Swanson17:06:20

Any advice for how to troubleshoot this kind of issue? Or maybe I'm going about the whole thing with the wrong approach

currentoor17:06:05

@chrisjswanson if you have keyframe mode on then every component on the screen should re-render, unless their props have not changed

Chris Swanson17:06:46

that's what i thought - so it means my props must not be changing for the component in question

currentoor17:06:01

yes that would be my guess, should confirm that first

currentoor17:06:07

do you have fulcro inspect installed?

currentoor17:06:55

using the element tab, try to inspect the component that contains the router

Chris Swanson17:06:05

ok let me try that, one sec

currentoor17:06:18

since that contains the props that ultimately go to the Profile component

currentoor17:06:39

if that doesn't work you can always fall back to good old js/console.log 😅

Chris Swanson17:06:36

cool, i didn't even realize that element inspector exists

Chris Swanson17:06:46

so the ident is not changing

currentoor17:06:11

yeah that was all @wilkerlucio, he's a beast

Chris Swanson17:06:30

well he has me appreciation 🙂

currentoor17:06:53

also, FYI i think the inspector is a great example application if you want to learn how to write fulcro apps

Chris Swanson17:06:03

so if the app db changed when i triggered the mutation, but the component ident didn't change, i must have something wrong with my router definition i guess?

Chris Swanson17:06:24

ok, good advice, i'll have to look at the code

Chris Swanson17:06:30

(for the inspector)

Chris Swanson17:06:16

the way that i'm using the router to swap by entity id - is that an intended usage?

Chris Swanson17:06:36

in the docs, it looks more like it's being used to switch between panels with fixed labels

currentoor17:06:07

the book does have an example of using the router with ids like the way you are doing it

currentoor17:06:29

you can see the live example (with source) at the end of that section

currentoor17:06:07

personally i typically use the routers for fixed labels and handle ids internally

Chris Swanson17:06:12

ok, yea i did read through that - it was the example that i used to build what i'm trying to fix at the moment.. just making sure i understood the usage correctly

Chris Swanson17:06:32

yea that's what i was wondering, if it's more common to just deal with ids yourself

Chris Swanson17:06:11

cause i also wasn't sure what the router passes to initial-state fn when it creates the component its routing to

currentoor17:06:20

i believe the router just passes some initial-state data that makes it's first key-value pair the default tab

Chris Swanson17:06:38

so if I have a router like the one i pasted above, and i route to update the id of the entity its showing , it should trigger that entity to update its ident and re-run its queries, right?

tony.kay17:06:55

Initial state is passed through the router, as long as you composed the initial state of the router to root

tony.kay18:06:47

in keyframe mode the full root query and render is run

tony.kay18:06:59

Yes, is the answer, but if you only have one Screen, a router is possibly overkill…unless you are using it to just get routing tree support

tony.kay18:06:45

The router is largely about minimizing query among multiple kinds of screens. If you’re just switching between data for the same screen, the router is just extra weight since an ident alone will do

Chris Swanson18:06:39

I see. Yea, i was using it in a routing tree, so i could switch a top level panel and then show the correct profile id.

Chris Swanson18:06:27

And that is actually working fine - going from "list" view to "details" view is ok. It's when I click a link to switch from one "details" view to another "details" view that I'm running into issues

Chris Swanson18:06:10

Namely, it seems, that the ident didn't actually change when I routed (well it changed in app db , but not in the routed component)

currentoor18:06:50

what does routing tree support mean?

Chris Swanson18:06:29

it's so i can issue a route name and have it be a set of route commands that update nested routers

Chris Swanson18:06:50

to ensure the top level router is showing the page that contains the sub-router which will select entity id

currentoor18:06:15

@tony.kay is there an advantage to using ids inside routers? vs just handling them on my own via queries and such?

Chris Swanson18:06:26

If I were to do it that way, I'd basically just have an entry in app db for "selected-profile" or something along those lines, and my component would query for that, right?

currentoor18:06:54

yeah that's what i was thinking

currentoor18:06:10

though i guess doing ids with a router does make things less ad hoc

tony.kay18:06:23

So it is fine to use IDs in routers, and the routing tree has :param/x support to fill them in

tony.kay18:06:52

It is possible, Chris, that there is a bug in keyframe mode 🙂 I’m not aware of it being used much by ppl, as it is a relatively new feature.

Chris Swanson18:06:38

as i'm exploring more, it seems possible

tony.kay18:06:40

but I’m pretty sure it works….so if you’re seeing the data update to the correct values, and not seeing it in the UI, I’d wonder if either (1) you didn’t enable it correctly or (2) it has a problem.

tony.kay18:06:15

Try adding a follow-on read of the join key of your router at the top and see if it fixes it

tony.kay18:06:22

If it does, it might be (1).

tony.kay18:06:31

If it doesn’t, then it might be (2)

Chris Swanson18:06:43

yea, i did try follow-on of :root/top-router

currentoor18:06:03

you have hot-reloading code setup for dev right? doesn't that trigger a root re-render also?

Chris Swanson18:06:04

no luck unfortunately

tony.kay18:06:20

And you’re sure your query in the subcomponent queries for id?

Chris Swanson18:06:31

i do have hot code reloading - could try to trigger it that way

tony.kay18:06:59

there is also prim/force-root-render!

currentoor18:06:35

i'm wondering if we should have a force-root-render button in the inspector?

tony.kay18:06:56

did you say :reconciler-options {:render-mode :keyframe} in your startup options?

Chris Swanson18:06:58

yea, i did it here:

(defn ^:export init []
  (reset! app (fc/new-fulcro-client
                     :reconciler-options {:shared    {::i18n/message-formatter message-format}
                                          :shared-fn ::i18n/current-locale
                                          :render-mode :keyframe} 
                     :started-callback (fn [app]
                                         (df/load app :profiles/all Profile
                                                  {:target [:list :profile :profile-list :profile-list/profiles]}))))
  (start))

Chris Swanson18:06:53

how can i get a reconciler to pass to force-root-render?

tony.kay18:06:01

from your saved app

tony.kay18:06:14

(get @app :reconciler)

Chris Swanson18:06:46

yea, so not even force-root-render seems to be working

tony.kay18:06:57

so, your data isn’t right somehow

Chris Swanson18:06:20

but when i use fulcro-inspect to see the element, the data is different that what the ui is showing

tony.kay18:06:44

to see the router? The router itself should have state…also, your target of load looks wrong to me

Chris Swanson18:06:27

i'm looking at the router's target, the "Profile" component

Chris Swanson18:06:00

what do you mean by "target of load" ?

tony.kay18:06:11

load :profiles/all

tony.kay18:06:16

isn’t that coimponent normalized???

tony.kay18:06:49

that target is too deep, and would not be targeted in the normalized component, which you may be mis-reading as “right” when in fact it is wrong

Chris Swanson18:06:12

ok, it seemed something wasn't right about that

tony.kay18:06:26

What is your ProfileList component’s ident?

Chris Swanson18:06:57

could i just show you the whole app somehow? it's just a page, a small app for learning

tony.kay18:06:12

sure, throw it up on a repo in github

Chris Swanson18:06:19

ok thanks, appreciate it

currentoor18:06:25

@chrisjswanson i'll take a look too, i'm curious and might be able to help

Chris Swanson18:06:03

awesome , appreciate the help

Chris Swanson18:06:14

hopefully i didn't do anything too boneheaded 😄

currentoor18:06:11

hopefully you did, that easier to debug 😉

currentoor18:06:35

i'll take a look in little in a bit, just need to finish some emails real quick

Chris Swanson18:06:48

sure, no rush, i'm around all afternoon

currentoor18:06:44

so the problem is when you click a specific profile it doesn't open?

Chris Swanson18:06:17

yea, the idea is it's like an address book, and a person can have emergency contacts

Chris Swanson18:06:35

i want the emergency contact to be a button to link to the other contact page

Chris Swanson18:06:10

what's really strange is that the subcomponent which shows the emergency contact info DOES get updated , but the rest of the profile data remains the same in the ui

Chris Swanson18:06:30

even though, upon inspecting it in fulcro inspector, it seems the component has the right data

currentoor18:06:47

let's take this chat out of the main channel simple_smile

tony.kay18:06:31

@currentoor It might be useful to post the final resolution to the channel…ppl might be interested

currentoor19:06:50

figured it out 😅

Chris Swanson19:06:07

yep, so the resolution:

currentoor19:06:10

turns out it was nothing related to state, routers, or data it a react DOM thing lol

currentoor19:06:28

@tony.kay Chris will post a little blurb about it

Chris Swanson19:06:38

I was using an input component with a defaultValue on it, but it didn't have a key param

tony.kay19:06:03

always glad to know it isn’t a bug 🙂

Chris Swanson19:06:09

yep , no bug 🙂

Chris Swanson19:06:48

so the behavior that i got was that the first time the component gets displayed, everything works correctly (because defaultValue gets used when it's first shown)

Chris Swanson19:06:34

but when I changed things, the props came through correctly to the component, but the input value didn't get updated. React ignored the new props, because the input's key hadn't changed

Chris Swanson19:06:19

in order to make it work correctly, i needed to supply a key that included a prop that I knew would change (ie the new entity id i'd routed to). that guarantees react updates the input when the route happens and the component props change

Chris Swanson19:06:50

big thanks to @currentoor for not just fixing the bug but helping me understand why it wasn't working, and teaching me some useful debugging tricks 🙂

currentoor19:06:18

happy to help simple_smile

currentoor19:06:44

FWIW @chrisjswanson you should still try to figure out why you get those random nils in your appstate

currentoor19:06:59

I'd bet money that they will bite you in the butt later...

currentoor19:06:56

consider making devcards for your components, helps isolate issues IMO

tony.kay20:06:55

That doesn’t sound quite right…if props change it updates. The key does not have to change. Key is only a bug if there are adjacent children with the same key….changing keys, however, will force react to re-render the component.

tony.kay20:06:17

but the :value changing on an input should cause it to refresh, key or no key…

currentoor20:06:38

@tony.kay he was using :defaultValue

currentoor20:06:50

had it been :value it would have updated regardless

tony.kay20:06:37

Oh, I see what you mean…yes, that is true

currentoor20:06:37

he was using :defaultValue because using :value would result in that flicker bug where the cursor jumps to the end of the input value

currentoor20:06:48

i know this more of a react thing but maybe we should have some explicit docs about inputs and tricks like using :defaultValue and :key

currentoor21:06:57

@tony.kay do you recommend using routers for modals?

currentoor21:06:18

semantic ui makes it easy not to

tony.kay21:06:46

Ah, so cursor jumping to the end is a different thing, and that should be already handled by Fulcro’s wrapped inputs. Should not see that unless you use a js ecosystem thing

tony.kay21:06:07

I do not use routers for modals.

tony.kay21:06:18

@chrisjswanson I think you were having cursor jumping problems if your keys were UNSTABLE

tony.kay21:06:06

that would cause the input to unmount and remount. You should definitely use :value, and should not end up seeing cursor jumping

tony.kay21:06:24

(unless your keys are unstable, in which case the unmount/mount will happen and cause you headaches)

tony.kay21:06:07

@currentoor I’m not aware of a :defaultValue :key trick to handle cursor jumping…not sure what you mean

currentoor21:06:28

using :defaultValue and :onBlur for inputs so you don't have mutations for every keystroke

Chris Swanson22:06:24

@tony.kay I'm using fulcrologic/semantic-ui-wrapper

Chris Swanson22:06:48

you are correct - the built-in fulcro dom/input doesn't have the cursor jumping issue

Chris Swanson22:06:29

the "defaultValue trick" that I'm using is to set the defaultValue of the component instead of the value , and then use onChange event to update its value in the app db. that way the input component's text value and the app db stay in sync , even though it's not a controlled input

Chris Swanson22:06:09

i wasn't using an unstable key; in fact I wasn't specifying a key at all . i did go back and try replacing "defaultValue" with "value" once I set the key , but alas the cursor jump bug returned

tony.kay22:06:43

So, I guess that’s ok…controlled inputs in semantic UI don’t tolerate the async update…they expect you to use component-local state for your control, which is screwy IMO

tony.kay22:06:04

but technically that’s what you have to do with “raw” inputs in react in general

tony.kay22:06:45

Fulcro works around that itself by wrapping all inputs in a way that tracks the changes in local state for you, so they are “semi-controlled”

Chris Swanson22:06:29

do you think the same technique could be applied to the semantic-ui-wrapper ?

Chris Swanson22:06:51

it does seem a little weird to have to use defaultValue

tony.kay22:06:22

In fact, I haven’t tried this, but I think you might be able to just use the wrap-form-element in dom.cljs as long as the control in question uses :value…worth a try

Chris Swanson22:06:24

i could give it a shot. i'm still pretty new to react, may be over my head. but if i can make it work, would you want the pr?

Chris Swanson22:06:04

interesting, i'll give that a try too

tony.kay22:06:07

Yeah, let me know how that goes…you’ll need to require the Semantic UI control via require, then pass that class to that function. It’ll return you a function that acts like a new factory for that control

tony.kay22:06:28

yeah, just tried it. Totally works fine

tony.kay22:06:39

here’s the relevant code (using Shadow-cljs):

tony.kay22:06:50

@chrisjswanson are you using shadow-cljs?

Chris Swanson22:06:57

still trying to get it to work

Chris Swanson22:06:24

are you using sui-factory?

tony.kay22:06:41

(ns app
  (:require     ["semantic-ui-react/dist/es/index.js" :refer [Input]]))

...

(def sui-input (dom/wrap-form-element Input))

(sui-input {:value v :onChange ...})

tony.kay22:06:01

Also: note that a LOT of react-semantic-ui wrappers do nothing more than emit DOM with a few classes. In those cases I strongly recommend just using dom and making your own little wrappers. That way you don’t have this headache, and SSR can work in the future for you if you need it.

tony.kay22:06:29

Even the active controls don’t have complicated logic. I ended up writing logic for some of them myself on my own project, just because I got tired of things like this

tony.kay22:06:47

The CSS itself is often trivial

Chris Swanson22:06:13

so just use dom to emit the correct elements with the semantic ui classes on them and it'll mostly work the same?

tony.kay22:06:15

I should probably update the fulcro semantic ui wrappers to say that, and deprecate it

tony.kay22:06:41

For grid, elements, and things that don’t have “logic” in them.

Chris Swanson22:06:51

ok, that makes sense

tony.kay22:06:01

when you get to dropdowns, modals, and such: those have logic…but it’s simple usually

Chris Swanson22:06:43

were you able to add label or anything to that sui-input ? it worked for me (gave me a blank input), but it doesn't seem to take other params

tony.kay22:06:02

and it just plays around with classes. I made my own autocomplete based on SUI dropdown…but their logic wasn’t working the way I wanted. Most of the work is in the CSS tweaking IMO, so I just wrote the logic myself. I had spent 2 days trying to make js ecosystem ones work the way I wanted. It took me 4 hours to make my own with their CSS…lesson learned

tony.kay22:06:25

OH….ooops…might need to use #js with it?

Chris Swanson22:06:59

yep you are right, #js

Chris Swanson22:06:44

good point, i probably couldn't made my own with dom in the time i spent fiddling around trying to fix that cursor jump bug

tony.kay22:06:45

Yeah, my bad 🙂

Chris Swanson22:06:02

no problem, really appreciate the advice

tony.kay22:06:21

in my experience I end up using MUCH less time making my own…esp for the ones that don’t have complex behaviors…even the autocomplete one with load went much better when I just rolled my own

Chris Swanson22:06:14

yea, maybe in time there could be a fulcro native ui widget library

tony.kay22:06:19

and you can “fix” the #js bit with:

(let [factory (dom/wrap-form-element Input)]
  (defn sui-input [props] (factory (clj->js props)))

Chris Swanson22:06:37

oh cool, nice 😞

tony.kay22:06:04

In time I’d prefer someone else maintain the CSS, so using semantic UI CSS, and having the logic coded in fulcro for the dynamic components would be nice.

tony.kay22:06:18

In general I’ve only had these kinds of struggles with things that take input

tony.kay22:06:28

and I’m glad I realized I already had a coded solution

tony.kay22:06:27

I want to be able to leverage the js ecosystem…so much out there, but so far I’m about 50/50 on sucessfully finding stuff worth using.

Chris Swanson22:06:45

right.. yea that is a hard tradeoff

tony.kay22:06:55

react-transition-group worked for me, as did a resize detection library.

tony.kay22:06:18

and a lot of (non-input) stuff in SUI works, but then anytime you render with external js SSR gets harder

Chris Swanson22:06:18

cause new users will want batteries-included, and we can't recreate everything. but some stuff is just a waste of time and it should be rebuilt natively

tony.kay22:06:54

I think everyone writing js struggles with the same crap…the ecosystem just has a lot of stuff that doesn’t quite work the way you’d like it to

Chris Swanson22:06:02

yea i ran into the ssr thing as soon as i started using SUI - had to add cljs tags all over the place, eventually just gave up on ssr

tony.kay22:06:29

I’m working on a project, and I’m using SUI, but I decided against using most of the react js for it…in cases where it is useful I write CLJC stubs that on the CLJ side can render a “reasonable” version of it via DOM, since SSR is usually just for initial load anyhow

Chris Swanson22:06:41

tbh i never even learned js , except what's been required to get my cljs working

Chris Swanson22:06:37

yea that makes sense , seems like a logical approach

tony.kay22:06:44

and I use the HTML Converter in the Fulcro Book to convert samples from the Semantic UI website to Fulcro DOM 🙂

Chris Swanson22:06:02

haha cool 🙂

tony.kay22:06:03

just inspect the page, play with the control, copy the elements, paste them in the converter…boom

tony.kay22:06:27

even “active” controls are easy to write, since you can see what CSS has to change over time that way

Chris Swanson22:06:30

i'm definitely gonna give that a try too , seems like it could sidestep a lot of headaches

tony.kay23:06:08

@chrisjswanson I just updated the fulcro wrappers to 2.0.0-beta2, and I added this wrapping in by default for the controls I found that took :value to make a controlled component

tony.kay23:06:15

seems to fix it in my devcard trials

tony.kay23:06:55

was an easy enough fix, and I didn’t realize it was doing the cursor jump thing…so this’ll make everyone’s experience better 🙂

tony.kay23:06:32

The only caveat to this “fix” is that if you hook up an onChange and don’t make it a controlled component, then you don’t really end up with a controlled component 😛 This wrapping “assumes” that when you set an onChange, you’re going to make your state match that of the input.