Fork me on GitHub
#fulcro
<
2018-04-11
>
piotrek20:04:17

Unfortunately I think this is related to https://reactpatterns.com/#function-as-children, not https://reactpatterns.com/#higher-order-component. The former works with actual component instances and the latter is wrapping component classes, not instances. Maybe I need to write Fulco version of the GoogleApiWrapper…

tony.kay20:04:34

HOC is just function composition, so if you pass a component in as a prop, you’re essentially apssing in a function…and in that case if you try to use a fulcro component you will have the binding problem that that function solves.

piotrek20:04:34

I’m sorry if I misunderstood - do you mean that I should use prim/with-parent-context in my ui-my-map? (https://github.com/pbzdyl/fulcro-google-maps-react-issue/blob/master/src/main/nes/ui/components.cljc#L46-L48)

piotrek20:04:49

If yes, then what should be passed as the first argument (context)?

tony.kay01:04:47

I don’t know the code, so it is a guess on my part. with-parent-context takes a parent component, so your ui-my-map would need to become a function that gets passed this from the calling component.

tony.kay01:04:29

actually, you’re not using fulcro components inside of the thing, are you? If it doesn’t have children, then I’m leading you down a bad path 😕

tony.kay01:04:54

Ah, I see someone else helped you. Sorry, I was a bit busy and was throwing a guess at you without looking very deeply

piotrek18:04:14

Actually, someone else helped me with something else 🙂 I didn’t know how to use GoogleAPIWrapper.

piotrek18:04:10

Now I’m still stuck as the props are not passed from component using ui-my-map to MyMap component wrapped with GoogleApiWrapper produced component

piotrek18:04:53

I tried it with with-parent-context but props are still not passed to MyMap component: https://github.com/pbzdyl/fulcro-google-maps-react-issue/blob/master/src/main/nes/ui/components.cljc#L46-L51

tony.kay18:04:11

show me the usage

tony.kay18:04:58

I missed the link

tony.kay18:04:20

Ah, I see. Your factory for MyMap is expecting CLJ Fulcro props, but the Maps API is a JS component…so it will send you raw js props

tony.kay18:04:55

or rather, the internals work that way…

piotrek18:04:38

I’m sorry but as I am still learning (and I’m coming from backend development) and I don’t know all the internals in React yet

tony.kay18:04:45

So, you can define a function as a component in React

tony.kay18:04:16

so, add a normal fulcro factory for MyMap, and then wrap a call to that in a conversion function…then use that conversion function instead of MyMap and it should work:

(def ui-map (prim/factory MyMap))

(defn my-map-wrapper [js-props]
   (ui-map (js->clj js-props)))

tony.kay18:04:44

If you’re going to do interop things, you’re going to need to learn a bit more about the ecosystem…I can’t isolate you from those kinds of details 🙂

piotrek18:04:51

Let me try it

piotrek18:04:15

I know - and providing hints where to search it really helpful - thank you!

piotrek18:04:47

I knew it wouldn’t be easy when I read in React docs about higher order components that it’s advanced topic 😉

tony.kay18:04:13

for a functional programmer, it’s trivial…You’re struggling with interop

tony.kay18:04:44

Components can be written as functions…therefore HOC is practically = HOF

tony.kay18:04:55

but you do have to get the arguments right

tony.kay18:04:42

As a function, HOC at the native JS layer is just a (fn [js-props] …)

tony.kay18:04:47

that get it?

tony.kay18:04:24

oh wait, you have more problems

tony.kay18:04:51

props needs wrapped in (clj->js)

piotrek19:04:31

Yes, gmaps contains my wrapper fns for React components from the js library and they are defined using factory-apply

tony.kay19:04:08

nvm…looks like you’re using DOM utils to do the conversion for you

tony.kay19:04:33

you getting props now?

piotrek19:04:14

So before applying your suggestions (using a fn as a component and have that fn converting js to clj props) I had my map being rendered but it wasn’t getting any props

piotrek19:04:57

Let me commit the new version - unfortunately with the new version the map is not loaded and only Loading placeholder is displayed

tony.kay19:04:31

but do your props print in the console?

piotrek19:04:09

This: ∅MyMap Props: {"children" ([]), "loaded" true, "google" {"maps" {…}}} Lat null Lon null

piotrek19:04:18

No props from parent fulcro component

tony.kay19:04:06

what does location look like in Stop?

piotrek19:04:57

facepalm it was nil…

tony.kay19:04:13

maybe some initial state is in order?

piotrek19:04:13

I’m so silly

piotrek19:04:55

I just put a static map {:lat 37.778519 :lon -122.405640} to ui-my-map-wrapped and I got the props

piotrek19:04:07

Let me fix the initial state issue first

piotrek19:04:18

MyMap component was missing :initial-state declaration

tony.kay19:04:47

so, is it all working now?

tony.kay19:04:19

I think you possibly have some refresh issues…the conversion from clj to js and back again will lose metadata

piotrek19:04:35

I fixed it and now the component gets the data but props contain string “lat” and “lon” keys - I fixed it by adding :keywordize-keys true to js->clj

piotrek19:04:14

So now MyMap gets the correct values bound to component args

piotrek19:04:25

But as you say I think there is a refresh issue

piotrek19:04:25

Could I trigger a refresh manually to confirm that it’s a refreshing issue?

tony.kay19:04:26

I think there might be. Fulcro puts timestamps on data for tree refresh

tony.kay19:04:51

are you seeing a refresh issue???…you don’t have mutations, so I’m not sure why you would (yet)

tony.kay19:04:52

but wait….the HOC should pass your data through unchanged…so I think my suggestion of the wrapper is not needed.

piotrek19:04:22

The GoogleWrapperApi adds logic to trigger gmaps js load. So it first displays Loading placeholder and once the gmap api is loaded and initialised it replaces the placeholder with MyMap component

tony.kay19:04:33

try passing the factory instead

tony.kay19:04:59

ui-my-map instead of the glue one

tony.kay19:04:07

and remove the conversion to js…actually, there isn’t one without the glue

tony.kay19:04:11

actually, there is, because you’re doing factory apply

piotrek19:04:07

Now I have this:

(def ui-my-map-wrapped
  (let [HOC (GoogleApiWrapper #js {:apiKey "AIzaSyDAiFHA9fwVjW83jUFjgL43D_KP9EFcIfE"}) ;; HOC is a fn: ComponentClass -> WrappedComponentClass
        WrappedMyMap (HOC ui-my-map)]                           ;; WrappedMyMap is a component class that wraps MyMap inside with some added logic for Google API initialization
    (utils/factory-apply WrappedMyMap)))

tony.kay19:04:07

so, depending on what the HOC component is really doing (it should just be proxying through the original props), your factory apply for it should not do a clj->js conversion

tony.kay19:04:29

if the HOC itself uses the props, then an additional hack will be needed

piotrek19:04:56

and I’m getting:

warning.js:34 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `d`.
    in d (created by nes.ui.components/Stop)
    in div (created by f)
    in f (created by nes.ui.components/Stop)
    in div (created by b)
    in b (created by nes.ui.components/Stop)
    in nes.ui.components/Stop (created by fulcro.client.cards/ui_23666)
    in fulcro.client.cards/ui_23666

tony.kay19:04:59

I’d put the CLJ props under a key like “cljprops”, and ensure that doesn’t get converted to js…then use the glue to pull it out unmolested

piotrek19:04:34

I think that maybe it would be much easier to just create my own Fulcro version of GoogleApiWrapper 🙂

tony.kay19:04:52

but this should not be that hard

piotrek19:04:59

and wrap all the Map, Marker etc inside such wrapper

tony.kay19:04:45

your goal is simply to pass through the unmolested CLJ props through the HOC. Your factory-apply defaults to a recursive conversion of the props.

piotrek19:04:13

do you mean (factory-apply WrappedMyMap)?

tony.kay19:04:43

convert-props

piotrek19:04:46

ok - I will try this - thanks!

tony.kay19:04:06

so if you instead had a passthrogh factory for HOC, you’d say something like `#js {“fulcroprops” props}

tony.kay19:04:18

that would get rid of the warning, and then you can use the glue to pull them back out

piotrek19:04:26

right - but where should I unwrap that?

piotrek19:04:35

I will try it now

tony.kay19:04:36

Right….BUT…were you already seeing a refresh issue?

tony.kay19:04:32

The props we got working caused a correct initial render, right?

tony.kay19:04:59

so yeah, this should help ensure that prop changes refresh things correctly

piotrek19:04:14

The lat, lon received from query were logged in the console twice

tony.kay19:04:44

you in devcards?

piotrek19:04:59

Yes, devcards

tony.kay19:04:07

then expect extra refreshes…

piotrek19:04:13

I changed it to use your trick

tony.kay19:04:16

swapping on the atom causes it, and cards do that

piotrek19:04:20

props are passed correctly

piotrek19:04:35

but still only loading placeholder is visible

piotrek19:04:16

I can see that the gmaps script is loaded in the network tab

tony.kay19:04:35

so from there it’s google’s API…don’t really know

tony.kay19:04:47

I thought you said initial render was right?

piotrek19:04:19

Yes, but the initial render will show just loading placeholder (and it will trigger GoogleApiWrapper to load gmaps script)

tony.kay19:04:37

OH…wait a sec…perhaps the HOC is trying to tack in API data in props somehow? Are you supposed to pass something through to the children?

piotrek19:04:15

Oh, yes, it passes “google” in props and I need to pass it to Map component

tony.kay19:04:32

so, you’ll need to perhaps do that (including passing it through the fglue)

piotrek19:04:42

Oh, and I’m not passig it through as I’m getting just clj props 🙂

piotrek19:04:56

so I need to add google from js-props to my clj props

piotrek19:04:17

It worked!

piotrek19:04:20

Thank you so much

piotrek19:04:33

I learnt a lot today

tony.kay19:04:56

yeah, I learned a little, too 😉

tony.kay19:04:38

You should contribute a blog post on integrating with HOC and Fulcro…I’d throw it up on the website 🙂

tony.kay19:04:15

or even add it to the dev guide

tony.kay19:04:29

react interop should be more detailed in the book

tony.kay19:04:33

As a generalization: You could write a function that can do all of that for you. Use goog.object/clone to clone the props you get, tack in the fulcro props under a key like “fulcroprops” with gobj/add (react props are read-only so you have to clone them to do that)…it would just be a function that takes a Fulcro component and returns a function that can be used in an HOC

tony.kay19:04:32

as long as your “made-up” key is not prone to collision, it should then be generally reusable. If you write it, I’d love to have a contrib. perhaps where I’ve got other react utils

piotrek19:04:21

That would be a good way to pay back for your great work on fulcro and your help

piotrek19:04:13

I don’t have a blog but I can come up with reusable code for working with HOC and provide some short doc in readme - then we could adapt it to the book or in other way

tony.kay19:04:59

that’d be great

piotrek19:04:01

I will let you know when I have something

piotrek19:04:15

Have a good day and once again thanks for your help!

piotrek08:04:11

Hello @tony.kay. I have create a gist covering the HOC topic: https://gist.github.com/pbzdyl/064272bece09cb3464b18d04f7246f50. I will add a reusable factory for working with HOCs later today or during the weekend. Any suggestions/comments are welcome!

piotrek10:04:35

Another update: I have added a reusable hoc-factory in the gist

tony.kay14:04:39

Thanks! I’ll see about integrating it somewhere.

piotrek15:04:13

Feel free to change it however you need it 🙂

tony.kay15:04:40

Could you open an issue on fulcro github and add a link to it? I don’t have time to do it right now, and Slack history will lose it quickly.

4
bbss09:04:41

Been playing with form support today, I got it all working decently, but one thing did confuse me.

(fs/entity->pristine* [:phone/by-id id])))))
in http://book.fulcrologic.com/#_selecting_an_entity_for_edit This is used in the abort, but also in the submit. I don't think that's an error as the comment above that line is seems to acknowledge a difference. And it could be that since a remote + refresh happens the form should update after a successful remote push. But I can't get that to happen. It might be my reads though, as my defquery-entity does not get hit. Later I noticed that it's surely not a mistake that the submit also requires the pristine->entity* to get called again as the abort breaks if I don't Additional tiny comment that I expected to be able to add-form-config* on a load. But I could make it work fine without, and that might add overhead/conflate things.

bbss10:04:42

I included a manual (df/refresh! this) in the callback after the transact! and now the update, abort work and my defquery-entity gets called. Still wonder if the (refresh [env] [[:thing/by-id 42]]) should have been enough.

wilkerlucio12:04:50

@bbss :refresh is about attributes, I'm not sure if idents work there, but AKAIK refresh uses the attribute index to find components and refresh those, idents are not there

bbss13:04:28

@wilkerlucio I see, passing attribute keys of the this/component passed to transact or of the root does not seem to refresh those either though.

exit215:04:14

Hi all, I’m working on an existing application that is written in untangled (and om). Is there a guide for upgrading to Fulcro?

tony.kay17:04:56

it has been so long, that I deleted it from the repository…but it is on the older tagged versions 🙂

tony.kay17:04:27

actually, you might use this version, in case something evolved…https://github.com/fulcrologic/fulcro/blob/2.4.3/README-fulcro-2.0.adoc

tony.kay17:04:09

If you have custom networking, that API changed slightly (the signature of the start method), but you might also consider porting to the newer fulcro-http-remote in networking which allows you to customize some things just via middleware.

exit217:04:17

great thanks Tony, I think this’ll serve as a good starting point

tony.kay17:04:47

If you go all the way to 2.5+, you get to remove all of those #js and dom props nils as well 🙂

exit217:04:00

yikes okay haha 😄

tony.kay17:04:25

I’d recommend just doing the base porting steps first, though

tony.kay17:04:42

there are some minor differences in how the 2.5 dom behaves, so isolate problems by doing them seperately

exit217:04:57

yeah I was thinking I’d go from the beginning and work my way up

tony.kay18:04:29

mainly it’s just renaming namespace usages. defui still works the same as it did. Oh, InitialState moved into primitives as well, so some of your protocols will have moved.

tony.kay18:04:08

it should go pretty rapidly with that rename-ns script

tony.kay18:04:19

@bbss what are you expecting refresh to do?

tony.kay18:04:03

I mean, you already did an optimistic update, so the UI should already be correct. What is there to refresh?

tony.kay18:04:33

By calling refresh! you’re asking for a remote query to run…that is not what a mutation’s refresh is about.

tony.kay18:04:16

refresh! is a function the runs a load. refresh (the list in a mutation) is the list of things that changed in the optimistic update that are not in the subtree that ran the tx (it is for UI refresh only of alternate UI not in scope of the transaction). Also, you really should not use refresh! from within a mutation or a post-mutation…if you need the server to give you back an update of the entity, I’d recommend using returning on your commit’s remote instead.

exit218:04:43

@tony.kay it looks like we are on navis/untangled-client "0.6.1" and org.omcljs/om "1.0.0-alpha47" - would your links still apply?

tony.kay18:04:56

just delete the untangled and om stuff

tony.kay18:04:08

make sure your other deps don’t pull them in (use exclusions if necessary)

tony.kay18:04:27

you front-end only?

exit218:04:28

no, we have navis/untangled-server "0.6.2" as well

tony.kay18:04:04

yep, drop that

tony.kay18:04:16

the server namespaces changed as well, but it’s all there

tony.kay18:04:46

fulcro.server and fulcro.easy-server…just look for the functions that go missing in one of those two

tony.kay18:04:16

When you move to 2.5+ the server stuff has dynamic dependencies, so you’ll have to add things like ring to your own project file

tony.kay18:04:50

and i18n has improved in a way that requires a few fixes (as of 2.4.3, I think)…if you use it, read https://github.com/fulcrologic/fulcro/blob/develop/README-porting-i18n.adoc

tony.kay18:04:25

Mostly API the same in UI, and actually makes things a lot easier (no more code splitting).

exit219:04:22

@tony.kay so is there a separate fulcro server dep/package or its namespaces and packaged with everything else?

cjmurphy20:04:52

[fulcro.client.dom :as dom] should (obviously) be able to be used in a cljc file (because it is a .cljc itself). When I do I get "No such var: fulcro.client.dom/macro-create-element".

tony.kay20:04:08

@cjmurphy It’s covered in the book. You need a conditional require

tony.kay20:04:51

@njj it’s all in fulcro…the later versions use a provided scope for ring and such

cjmurphy20:04:22

Yes was thinking that, good... 🙂

tony.kay20:04:45

it’s only covred in the localized dom, but you need that sort of thing in your own cljc file

tony.kay20:04:00

(just drop the localized-)

tony.kay20:04:06

I’d love a patch to the book if you feel like it..maybe in the early parts of http://book.fulcrologic.com/#_component_rendering

cjmurphy20:04:32

Yes this works: #?(:clj [fulcro.client.dom-server :as dom] :cljs [fulcro.client.dom :as dom]). Yes you can use all the 'missing #js' new dom stuff regardless of whether you are using any of the localized css. That is not really clear in the book. It's the least I can do...

Daniel Hines23:04:31

I want to deploy a project created with the lein template to Heroku. I have credential secrets that thus far have lived in defaults.edn. Should I lift those into environmental variables, or is there an easy way to get a configuration file into a Heroku build after the fact?

tony.kay23:04:06

no, you’ll have to deploy with a resource-based config

tony.kay23:04:22

you can use :env.edn/PORT to get the serving port in there