Fork me on GitHub
#reagent
<
2018-05-30
>
theeternalpulse15:05:24

How do I point a cursor to the result of a function instead of a property. I have a javsacript Date object and I want a cursor pointing to each of their time components.

jmckitrick15:05:50

I have a colleague who is working with Reagent for the first time. We are using the ‘react-select’ component: https://github.com/JedWatson/react-select

jmckitrick15:05:27

He needs to override the optionComponent, and I’m trying to figure out how to do that.

jmckitrick15:05:11

When I return a basic reagent component for that property, I get A valid React element (or null) must be returned

jmckitrick15:05:58

Do I need some magic combination of adapt-react-class and/or reactify-component? I’ve not used these before….

gadfly36115:05:57

@theeternalpulse I don't think you can have a cursor point to the result of a function. A cursor can just access a nested map by their keys. I think you'll have to have the date object be returned by the cursor ... And then with that, use helper functions to access whatever you want.

jmckitrick16:05:45

I didn’t know that existed, actually.

pesterhazy16:05:52

you'll need something like #(r/as-element [:div ...])

jmckitrick16:05:53

Only the basic reagent page

justinlee16:05:57

the “valid react component” error is because you’ve returned a function or a hiccup rather than a react component

jmckitrick16:05:05

I’ll try it.

jmckitrick16:05:33

Hmm. Same error.

justinlee16:05:38

@pesterhazy why the #--won’t that return a function?

justinlee16:05:51

i think you just need (r/as-element [:div ...])

pesterhazy16:05:04

right, I assumed that it expected a component

pesterhazy16:05:27

but maybe that's not the case

jmckitrick16:05:35

Wait! It worked

jmckitrick16:05:51

I had to move some things around…

jmckitrick16:05:59

Let me read that guide now….

justinlee16:05:27

one problem with react documentation is that it isn’t precise with the words “element” and “component” because the JSX makes it magically work

4
justinlee16:05:03

@jmckitrick if there is a pattern in there that isn’t covered let me know

justinlee16:05:13

i want that guide to cover the common cases by example

pesterhazy16:05:15

maybe an example of a popular react library (like react-select) would help

jmckitrick16:05:27

So the docs for react-select, as well as the example we wanted to emulate, both refer to optionComponent prop as a React class.

jmckitrick16:05:57

But @lee.justin.m the docs you linked me to say the as-element solution is for components expecting a function.

jmckitrick16:05:05

So I’m still confused 😕

jmckitrick16:05:52

For the record, we have react-select working great with the existing reagent import and shadow-cljs setup. But the customization is bringing new challenges.

pesterhazy16:05:21

a function can be a React component ("functional components")

justinlee16:05:38

that’s interesting that it worked. i might have expected it to take the result of reactify-component actually

pesterhazy16:05:03

in that case you're basically short-circuiting Reagent

pesterhazy16:05:23

reactify-component should work as well (and give you Reagent-style access to the props)

jmckitrick16:05:53

@lee.justin.m that’s the route I was going, actually.

jmckitrick16:05:08

Let me try again….

pesterhazy16:05:29

@jmckitrick if you want to help improve the docs you could contribute an example for react-select

jmckitrick16:05:41

I’d be happy to!

pesterhazy16:05:41

even just putting it up as a gist would be helpful

jmckitrick16:05:55

Once I understand why this works the way it does.

justinlee16:05:58

the other thing that makes this confusing as hell is that both reagent and react do certain kinds of conversions for you which makes it hard to form a mental model

pesterhazy16:05:59

PR for the docs folder is even better

jmckitrick16:05:12

reactify-component did not work, while as-element did

pesterhazy16:05:25

can you show the code?

pesterhazy16:05:44

sure (as a code snippet)

jmckitrick16:05:49

(defn react-select-component []
  [:div {:style {:width "40%"}}
   ;; Syntax sugar for NPM components.
   [:> (.-default react-select)
    {:name "form-field-name"
     :value @(rf/subscribe [:subs/selected-destination])
     :onChange #(rf/dispatch [:events/select-destination %])
     :optionComponent my-custom-renderer
     :multi true
     :simpleValue true
     :options @(rf/subscribe [:subs/destination-data])}]
   [dl-button-component]])

jmckitrick16:05:51

(defn my-custom-renderer [args]
  (reagent/as-element
   [:div "foo"]))

pesterhazy16:05:59

does this work?

pesterhazy16:05:02

(defn my-custom-renderer* [args]
  [:div "foo"])

(def my-custom-renderer (r/reactify-component my-custom-renderer*))

jmckitrick16:05:17

Let me try… oh, and I found in those interop docs a part that more accurately describes the use case… Creating React Elements from Hiccup Forms because since this component displays options, it’s displaying children.

jmckitrick16:05:24

Let me try your idea, @pesterhazy

jmckitrick16:05:22

Yes, it works!

jmckitrick16:05:55

But it didn’t work when I used reactify-component inside the component function like as-element

pesterhazy16:05:11

Yeah that's not how it's supposed to be used

pesterhazy16:05:46

It takes a Reagent component function (fn returning hiccup) and transforms it into something digestible by vanilla React

jmckitrick16:05:33

So I had it between the fn and hiccup, rather than around them both.

pesterhazy16:05:00

Also using defs helps avoid re-renders

jmckitrick16:05:06

This is my first foray into interop beyond [:> foo-component]

pesterhazy16:05:39

Yeah it's not rocket science, but the docs still don't cover everything

jmckitrick16:05:45

I actually missed that detail

pesterhazy16:05:17

The good news is anything you can do with React, you can do with Reagent as well

👍 4
jmckitrick16:05:08

I knew that was the case, but had to convince a colleague we could find the answer in a reasonable time 😉

jmckitrick16:05:27

Fortunately, the support here is fantastic.

jmckitrick16:05:39

Is there something I can contribute to the docs to ‘give back’ in some way?

jmckitrick16:05:59

Or was this just an error on my part, rather than a needed clarification?

pesterhazy16:05:25

A section on how to apply the "customize renderer" pattern in Reagent would be great IMO

jmckitrick16:05:56

In the interop guide?

pesterhazy16:05:57

Like optionComponent

pesterhazy16:05:13

This pattern comes up in many React (and ReactNative) libs

pesterhazy16:05:35

Yeah there, or maybe add an Advanced Usage section. What do you think @lee.justin.m?

justinlee16:05:54

i would just add a new section

pesterhazy16:05:01

It's linked in the readme, but maybe not prominently enough

justinlee16:05:33

like in the examples section

pesterhazy16:05:53

Once the dust settles we might link there in the Readme maybe

justinlee16:05:23

@pesterhazy I was just talking to @martinklepsch about why his version renders so much better than http://reagent-project.github.io/docs/master/

pesterhazy16:05:28

The cljdoc API docs for Reagent are better than what we currently have

pesterhazy16:05:39

They include source links for example (super useful)

justinlee16:05:24

@juhoteperi recently added https://github.com/reagent-project/reagent/blob/master/doc/cljdoc.edn but it still renders terribly on the reagent homepage

justinlee16:05:48

maybe we should just swap out the link and point to cljdoc

pesterhazy16:05:25

Although the cljdoc page should also link to Dan's NEWS style blog posts like http://reagent-project.github.io/news/news051.html

justinlee16:05:28

before we do that though we need to fix the docstrings to be in markdown. right now they are plaintext and assume the rendering will be monospace, which cljdoc doesn’t do yet

pesterhazy16:05:35

They include crucial information as well

justinlee16:05:44

in theory, i have collected everything in those news posts

justinlee16:05:54

virtually all of that should be in /docs

pesterhazy16:05:56

That's fantastic

martinklepsch16:05:25

I’m considering adding the ability to hide items in the sidebar so you could have a news page that links to the various individual posts…

pesterhazy16:05:28

We may want to rename the /docs mds from Tutorials to just Docs or Guide

justinlee16:05:30

I definitely had it all in a single document. @mikethompson wanted to keep the docs separate so @gadfly361 merged it, but I think 90% of that stuff made it in

pesterhazy16:05:55

Tutorial isn't really the right word for documentation explaining essentials

justinlee16:05:08

cljdoc kind of solves the problem of separate vs. unified for us so that’s super convenient

pesterhazy16:05:59

Yeah it feels like a better entry point for new users

pesterhazy16:05:15

What I'm missing is a short abstract of what Reagent actually is

justinlee16:05:54

my original intent was to have something in between “tutorial” and “docstring”: something like a definitive user manual that more completely describes reagent’s rather complicated mechanisms.

justinlee16:05:54

we’re like halfway there

pesterhazy16:05:44

Why not just call it what it is, a User Guide?

justinlee16:05:49

yea that’s definitely the idea

justinlee16:05:38

the questions have been: where does it go? how is it going to be formatted? how do we deal with mike’s more tutorial-like documentation vs. more formal and complete descriptions?

pesterhazy16:05:09

I meant just renaming what we already have to User Guide

pesterhazy16:05:59

The docs Mike and you contributed from a great basis to start from

pesterhazy16:05:06

We can always improve things from there

justinlee16:05:06

right now there’s literally no place to title it because it’s spread out over a bunch of docs. (in my gist I had it in a single file titled “Reagent User Manual” with a TOC.) if we were to move to cljdoc and get that all formatted nicely, then it will be obvious: all of the “guides” will be at the top and then you can drill down into the docstrings.

hagmonk19:05:34

if I want to coordinate a join between two http requests, what's the best way to do that with ratoms? I get some timeseries info from one API, then need to look up some dimension values in another API … just chain a bunch of ratoms together with track?

justinlee20:05:17

@hagmonk You mean, how do you coordinate issuing a second http request once the first has returned?

hagmonk20:05:19

yeah so the situation is I get some results back from one API, then to look up people's names I need to hit a second API … then join both results together, and likely put it in a third ratom (or cursor). I'm just wondering what the "reagent-y" way of doing this is

justinlee20:05:19

i mean, i would definitely not use ratoms for coordinating asynchronous communication. the problem with doing that is that shit will just start happening and you’ll have no idea why. best to use ratoms for one thing: triggering re-renders

justinlee20:05:05

if you need to coordinate multiple http calls, I would recommend using a promise-based solution. core.async would work too, though I personally am not a fan

hagmonk20:05:06

that is super helpful to know, the boundaries around ratoms, the "hey don't do that with ratoms" moments, aren't yet clear to me

hagmonk20:05:48

(I reach for core.async on the server a lot but only when the fiddleyness justifies it)

justinlee20:05:31

i personally think the observer pattern is really neat, but it is also dangerous because it gets rid of predictable stack traces and runs the risk of shearing cause and effect. in other words, some handler changes a data value, then a bunch of magic happens, and then some watching code fires. it can be very hard to work backwards from that if it goes wrong.

💯 4
justinlee20:05:47

core.async has too much complexity and much of it is really geared to solving multithreading problems, in my opinion. it also does not handle exceptions in a developer friendly way. I now use the promesa library with cljs-ajax. it’s simple and predictable. you just do (-> (first-request) (then (fn [result] do-next-thing)) (catch (fn [error] ...))

justinlee20:05:15

if you need more complex things the alet macro is pretty powerful

hagmonk20:05:34

Oh I haven't seen cljs-ajax

justinlee20:05:04

its just a slightly nicer wrapper around xhr

hagmonk20:05:17

I'm trying to settle on a frontend/backend stack that plays well together … it seems like the "fancy" pipework is to do it all over websockets with sente or something, but my backend preference is pedestal + jetty

hagmonk20:05:43

so maybe just cljs-ajax and pedestal and I'll HTTP POST my way to success ;)

justinlee20:05:15

if you’re going to use websockets you can use promises there too. you just need some websocket client that gives you a place to put callbacks. then you just stick resolve and reject from promesa/promise there and boom you are in the promise land

justinlee20:05:39

i’d like to use websockets at some point to but i’m sticking to good ol’ post until i have time to deal with that

hagmonk20:05:00

yeah I can't really justify it until I'm trickling updates into the frontend, which isn't very high on my feature list

kwladyka22:05:16

(def grid (r/adapt-react-class Grid))
[grid {:container true}
"foo]
;; I want to create grid-container which do something like that
(def grid-container [grid #(merge {:container true} %)])
;; So I could use it in that way to not write each time :container true
[grid-container {:style {:background-color "red"}}]
How can I achieve it?

justinlee23:05:28

@kwladyka this: [grid #(merge {:container true} %)] passes a function to grid instead of a map

justinlee23:05:38

i’m not quite sure what you are trying to do or what is wrong

justinlee23:05:44

maybe what you wanted to do is (defn grid-container [props] [grid (merge {:container true} props)])