Fork me on GitHub
#reagent
<
2017-12-20
>
souenzzo00:12:42

is someone using the concept of parser from #om with reagent?

Mikko Koski07:12:43

If I have a list of components, I need to give each component a unique key in order to avoid the React warning. I was wondering, is it ok to convert the list of components to vector using into? That way I don't have to give them the unique key. Like this:

(defn my-container [& content]
  (into [:div {:class "my-container-class"}] content))

(defn page []
  [my-container
   [:div "Div 1"]
   [:div "Div 2"]
   [:div "Div 3"]])
Are there any downsides for this approarch, e.g. performance?

justinlee07:12:51

@rap1ds Does that actually avoid the warning?

justinlee07:12:06

Doesn’t that just push the warning up one level?

justinlee07:12:29

The basic point is that react won’t re-render an item in a list if it has the same key, even if something is inserted or deleted, or if the order changes. If none of those things will ever happen with your list of elements, it is totally fine to give react the index as a key.

Mikko Koski07:12:50

@lee.justin.m Yeah, the converting to vector with into seems to get rid of the warning. Just tried.

Mikko Koski07:12:56

Indeed, the items in this case are static. Nothing is added or removed dynamically. Yeah, I see your point about giving the index as a key. Maybe I should do that instead.

justinlee07:12:46

Yea if they won’t change then using an index is perfectly fine. There is no difference unless a mutation is possible.

justinlee07:12:11

I think that what you did with into will avoid the warning but not the actual problem. I think the warning is a heuristic that only pops up when you have a component with a bunch of elements that are the same type. But I don’t 100% understand what the deal is.

justinlee07:12:25

Either way, if no mutations are possible, it doesn’t matter.

Mikko Koski08:12:29

Cool, thanks!

eveko14:12:17

I have an ajax call for a ui component. Problem is that ajax call is repeated nonstop and if I use dev tools my memory use will go up and to a grinding halt.

eveko14:12:54

how could I alleviate this?

venantius14:12:58

I’m trying to render a chart using recharts, but I’m not sure how to pass the component to Reagent.

venantius14:12:18

Ostensibly in JSX the component would look something like:

<LineChart
  width={400}
  height={400}
  data={data}
  margin={{ top: 5, right: 20, left: 10, bottom: 5 }}
>
  <XAxis dataKey="name" />
  <Tooltip />
  <CartesianGrid stroke="#f5f5f5" />
  <Line type="monotone" dataKey="uv" stroke="#ff7300" yAxisId={0} />
  <Line type="monotone" dataKey="pv" stroke="#387908" yAxisId={1} />
</LineChart>

venantius14:12:47

so, naively, I thought maybe something like this would work:

venantius14:12:49

(defn my-component
  []
  (reagent/create-class
    {:reagent-render
     (fn []
                 [:BarChart {:width 500 :height 300 :data [{:name "a" :value 12}
                                                    {:name "a" :value 24}]}
           [:CartesianGrid {:strokeDasharray "3 3"}]
           [:XAxis {:dataKey "name"}]
           [:YAxis]
           [:Tooltip]
           [:Legend]
           [:Bar {:dataKey "pv" :fill "#8884d8"}]
           [:Bar {:dataKey "uv" :fill "#82ca9d"}]
           ])}))

venantius14:12:03

(this is using React 16 and Reagent 0.8.0-alpha2)

eveko14:12:13

are these react components?

eveko14:12:42

did you try adapt-react-class?

venantius14:12:53

I did not even know about adapt-react-class! 🙂

venantius14:12:56

where can I learn more?

eveko14:12:43

then you can use it like you did up there

eveko14:12:07

you need to require the react library you are using though

eveko14:12:33

and always provide an empty map {} to adapted react components

venantius14:12:54

recharts has a cljsjs port that I’m requiring in the ns

venantius14:12:13

could you clarify what you mean about the empty map?

eveko14:12:58

so react components have properties, you put them in the map

eveko14:12:37

for example

eveko14:12:10

sometimes wierd things happen if you don't include an empty map

eveko14:12:03

in my example i just have a name property, but if i did not want it, i would have passed an empty map

venantius14:12:33

okay, gotcha

venantius15:12:27

more questions

venantius15:12:36

is the idea that I’d do, e.g.

venantius15:12:52

(def BarChart (r/adapt-react-class cljsjs.recharts/BarChart) ?

eveko15:12:10

then you use it [BarChart {}]

adammiller15:12:08

for reagent version later than 6.0 you can just use shorthand syntax of [:> cljsjs.recharts/BarChart]

seako21:12:32

you beat me to it! i was gonna say this very same thing.

eveko15:12:33

😆 is shorthand for adapt?

eveko15:12:47

could you have a look at my question above? I seem to get a memory leak due to an ajax call

venantius15:12:35

(this seems to be working, thanks gang!)

adammiller15:12:52

everytime comps changes it's going to cause components to re-render which of course will call your ajax call again, causing comps to change.... You have an infinite loop essentially.

adammiller15:12:04

Not sure what the intention is, but components would need a render function. The ajax call should be called outside that render function.

eveko15:12:50

Well, it is sort of fine this way. If a new component is added to the database, it will instantly appear in the front end

eveko15:12:09

the problem is, that when I want to used dev tooling from the browser

eveko15:12:29

it tracks network activity wich eats memory in the log

adammiller15:12:52

perhaps but it is going to repeatedly fire that ajax call. If you want that real time behavior I'd move the ajax call outside of that components function and into something that fires it at more set intervals. then in your components function in the componentdidmount you can start the polling and on the unmount stop it. Or for even more realtime change it to using websockets but guessing interval polling would be good enough.

adammiller15:12:11

something more like this perhaps

adammiller15:12:15

(defn components []
  (let [comps (r/atom nil)
        _ (start-polling (fn [result]
                           (swap! comps assoc :data result)))]
    (fn []
      [:div.main.components
       (map makecomponent (get @comps :data))])))

adammiller15:12:31

where in start-polling you'd do a window.setTimeout and do your ajax call

eveko15:12:26

what does the _ do in the let vector?

adammiller15:12:52

if you don't care about return value you can use an underscore

eveko15:12:58

good to know

juhoteperi15:12:00

@eveko It is a naming convention (for unused bindings), _ is a normal symbol, you could use it to refer to the value (but that would be bad idea)

eveko15:12:12

I am getting unexpected identifier errors 😕

eveko16:12:28

if fixed the ajax call, but reagent is not rendering the components

eveko16:12:51

functions are not a valid react child

adammiller16:12:51

looks alright (I haven't tried it locally). Are you handling the nil case and returning an empty component in makecomponent?

eveko16:12:38

i dont think so

eveko16:12:47

but it seems that the leak is not fixed

eveko16:12:58

it just makes multiple timers

adammiller16:12:17

what's the code look like that calls components-t?

eveko16:12:02

main is called after a secretary dispatch after an authorization call has been made

eveko16:12:17

the first render is an empty div

adammiller16:12:04

tried this locally and it seems to work alright (just hard coded response from the ajax fn). Although technically to do this you'd want to probably bind the result of js/setInterval to a local var and clear it before creating new one.

adammiller16:12:09

(defn get-component-data2 [f]
  (f [[:div "hello"]]))

(defn start-polling [fun]
  (js/setInterval (fn [] (get-component-data2 fun)) 5000))

(defn makecomponent [c]
  (println c)
  (if (some? c)
    c
    nil))

(defn components-t []
  (let [comps (reagent.core/atom nil)
        _ (start-polling (fn [result]
                           (swap! comps assoc :data result)))]
    (fn []
      [:div.main.components
       (map makecomponent (get @comps :data))])))

eveko16:12:39

isn't swap taking care of the clearing?

adammiller17:12:35

your interval created with js/setInterval is going to continue running so each update to the components is going to create a new one (but the old one's will still be running) unless you store it in a var and run js/clearInterval(my-interval)

eveko17:12:03

that is the issue I am having now

eveko17:12:27

but how should I clear the interval then?

adammiller17:12:14

i'd probably just use a regular atom....something like this:

adammiller17:12:17

(defonce interval (atom 0))

(defn start-polling [fun]
  (js/clearInterval @interval)
  (reset! interval (js/setInterval (fn [] (get-component-data2 fun)) 5000)))

adammiller17:12:46

cleaner option once you get it all sorted out would be to utilize your main components react lifecycle methods and return the interval id created in the componentdidmount and store that in your component state. Then in componentdidunmount you would call the clear. That would keep the code a bit more cohesive.

eveko17:12:26

I have no experience directly working with componentdidmount

eveko17:12:41

I just start a reace/render and go from there

eveko17:12:49

reagent/render*

adammiller17:12:06

you migh thave a look through this....it shows you the different ways of creating reagent comps. Last way giving you full control of the react lifecycle methods: https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components

eveko17:12:41

In the end I just decied to globalize the data

eveko17:12:58

I am planing to use the data in other ui elements in the future, so this works fine for now

justinlee17:12:36

Do folks have a favorite library for making xhr requests? I’m starting to use cljs-http, which seems fine, but wondered if others have experience with other libraries.

adammiller18:12:52

That's probably the most common other than just using google closure's xhr stuff directly, but cljs-http uses it under the hood.