Fork me on GitHub
#reagent
<
2018-09-04
>
jplaza01:09:00

Hi everyone! Not sure if this is the right place to post this, but hopefully someone else has bumped into a similar problem. I’m starting a new project now, it’s a Maps app. I started with reagent, but I’ve already lost 2 hours trying to import the latest version (2.0.1) of react-leaflet with no success 😢 I went the :npm-deps route and also tried by including it directly using a <script> tag. It seems to be something that needs to be solved with externs but also tried that with no success. I was digging into react-leaflet and they have a strange (at least for me) of loading Any tip on what to do or where to look?

gadfly36101:09:51

What version of react-leaflet and what version of reagent are you using. Also, could you provide a snippet of what the error in the console looks like

jplaza19:09:33

reagent 0.8.1 and react-leaflet 2.0.1 importing it like this

(ns my.app
  (:require [react-leaflet :as leaflet]))
if I look into the imported var it’s and object with keys bein Map, Marker, etc, but all of them are have undefined as the value. I also tried to with :infer-externs true compiler option @gadfly361

abdullahibra10:09:44

that's what i have tried to do but i got errors

abdullahibra10:09:01

what do you think guys about what i miss?

abdullahibra10:09:02

or even how can i debug this more?

oliy10:09:42

Hi, I was wondering if anyone knows why the following doesn't work (apologies I have had to type this on my phone)

(def foo
  (with-meta
    (fn [bar]
      [:div bar])
    {:component-did-mount
     (fn [this]
       (let [props (reagent/props this)]
         (js/console.log "props are nil" props)))}))

oliy10:09:36

However I can get the props as

(fn [this]
  (let [props (rest (.. this -props -argv))]
    ...))

oliy10:09:25

If I use reagent/create-class then reagent/props works as expected inside the lifecycle fns

oliy10:09:40

Just a bit surprised!

pesterhazy11:09:01

Using metadata to express lifecycle methods is an antipattern and deprecated-ish, so I'd just go with create-class

abdullahibra11:09:20

@pesterhazy can you please check my error/

oliy13:09:42

Thanks @pesterhazy wasn't really aware of that

abdullahibra14:09:41

what are common react input validations you guys use within your apps?

Petrus Theron15:09:34

How can I use r/with-let and r/track to call setInterval when the desired interval time changes? E.g.

(defn game-container [!state !tick-interval]
  (r/with-let [_ (js/window.addEventListener "keydown" handle-keys!)
               tick! #(if (and (not @!dead?) (not @!paused?))
                        (swap! !state next-state))
               interval (js/setInterval tick! @!tick-interval)]
              [world-view @!state]
              (finally
                (prn "clearing interval" (:tick-interval @!state))
                (js/clearInterval interval)
                (js/window.removeEventListener "keydown" handle-keys!))))
Mounting this container does not re-mount the component when !tick-interval changes.

pesterhazy15:09:09

@petrus, the code is a bit confusing, you're doing multiple things at once

Petrus Theron15:09:32

It's a game of Snake that lives here: https://theronic.github.io/cljs-snake/ I'm subscribing to key events, setting an interval to update the game state every tick-interval milliseconds, and rendering world-view with the current !state of the world. If you can write a clearer container component for managing game logic and state mutation, I'd like to see it.

pesterhazy15:09:37

Here's the thing: components are the V of MVC, they shouldn't care about changing state at all - in most cases

pesterhazy15:09:27

Think about the setInterval timer as independent of the React components

pesterhazy15:09:33

probably best not to (ab)use reactions for side-effects

Petrus Theron15:09:03

As far as I can tell, with-let is billed for exactly this kind of lifecycle management: https://reagent-project.github.io/news/news060-alpha.html

pesterhazy15:09:49

right, with-let is fine, but I'm not sure about passing in ratoms as a means to trigger side effects

Petrus Theron15:09:32

It's a game of Snake that lives here: https://theronic.github.io/cljs-snake/ I'm subscribing to key events, setting an interval to update the game state every tick-interval milliseconds, and rendering world-view with the current !state of the world. If you can write a clearer container component for managing game logic and state mutation, I'd like to see it.

justinlee15:09:25

@petrus > Mounting this container does not re-mount the component when !tick-interval changes. ^ this is right and that’s as expected. if you want to force a re-mount when things change, you can pass give the component a key equal to !tick-interval which will force a remount

Petrus Theron15:09:07

thanks, is it possible to return the ^{:key ...} from inside the game-container component?

justinlee15:09:05

mount/unmount and update are distinct events in reagent/react

justinlee15:09:38

i’m not sure i understand the question

Petrus Theron15:09:17

Instead of ^{:key @!interval} [world-view some-state] (which works beautifully!), can I set :key from inside world-view?

justinlee15:09:08

by the way, there is another paradigm for this kind of code where you need to use a component to cause a side effect but don’t want to render anything: you can just cause the side effect in the render function itself (but you’ll have to do a check to see if the new value is different)

justinlee15:09:44

this is used in things like sound controls where you are interacting with the sound ui of the dom

pesterhazy15:09:48

[world-view {:key 123} some-state] is my preferred way of setting the react key

Petrus Theron18:09:37

is that a feature or convention for passing attrs down into child component?

justinlee15:09:16

the key thing works but it is kind of clunky

pesterhazy15:09:40

yeah it's hacky as hell to rely on key to force rerenders

pesterhazy15:09:47

components lifecycle methods are not there for controlling state; they're escape hatches for perf optimization

pesterhazy16:09:18

I'd write an (defn init []) function that gets the timers (and other resources) started

pesterhazy16:09:01

init gets called once on startup

pesterhazy16:09:52

game state gets updated in the timer callbacks, not in the components. For all this "controller" part of the app cares, the game board might as well not even be rendered at all

Petrus Theron18:09:03

in my case, the tick-interval is affected by the state of the game (game runs faster at higher levels), and my game-component is playing the role of init for game-related concerns

pesterhazy19:09:05

How about an init-level function that sets the timer?

pesterhazy16:09:06

anyway that's how I would structure it </rant>

javi16:09:21

context: exploring serializing some data types is there a way to rebind the private pr-atom function that RAtom IPrintWithWriter uses? cheers!

javi16:09:49

or should i just overwrite the IPrintWithWriter implementation?

Petrus Theron18:09:37

is that a feature or convention for passing attrs down into child component?

Petrus Theron18:09:03

in my case, the tick-interval is affected by the state of the game (game runs faster at higher levels), and my game-component is playing the role of init for game-related concerns

jplaza19:09:33

reagent 0.8.1 and react-leaflet 2.0.1 importing it like this

(ns my.app
  (:require [react-leaflet :as leaflet]))
if I look into the imported var it’s and object with keys bein Map, Marker, etc, but all of them are have undefined as the value. I also tried to with :infer-externs true compiler option @gadfly361

justinlee19:09:44

@petrus the basic thing you have to come to grips with is that react components do not remount when their props change (by design). so the with-let isn’t going to work because with-let is a clojurey way of attaching code to the component’s constructor and to its componentWillUnmount lifecycle method. what you need to do instead is deal with changing props either in the render method or by using a form-3 component and dealing with it in componentDidMount and in componentWillUpdate

justinlee19:09:30

the key thing I mentioned is an exception: if you change the key your force a remount

justinlee19:09:52

i don’t really think that’s the right approach, but it is closest to what you were doing and I can’t stand it when I ask a question and people say “don’t do that” 🙂

pesterhazy19:09:52

I stand by my "don't do that" answer but here's an example of a component that reacts on changing props while mounted: https://github.com/pesterhazy/cljs-spa-example/blob/master/src/cljs_spa/router.cljs#L47

pesterhazy19:09:55

fwiw I also don't particularly like with-let, it diverges too much from React by using trickery to avoid lifecycle methods

pesterhazy19:09:32

@jplaza what does (js/console.log leaflet) print out?

jplaza21:09:14

{…}
AttributionControl: (...)
Circle: (...)
CircleMarker: (...)
ControlledLayer: (...)
DivOverlay: (...)
FeatureGroup: (...)
GeoJSON: (...)
GridLayer: (...)
ImageOverlay: (...)
LayerGroup: (...)
LayersControl: (...)
LeafletConsumer: (...)
LeafletProvider: (...)
Map: (...)
MapComponent: (...)
MapControl: (...)
MapLayer: (...)
Marker: (...)
Pane: (...)
Path: (...)
Polygon: (...)
Polyline: (...)
Popup: (...)
Rectangle: (...)
ScaleControl: (...)
TileLayer: (...)
Tooltip: (...)
VideoOverlay: (...)
WMSTileLayer: (...)
ZoomControl: (...)
withLeaflet: (...)
get AttributionControl: ƒ AttributionControl()
get Circle: ƒ Circle()
get CircleMarker: ƒ CircleMarker()
get ControlledLayer: ƒ ControlledLayer()
get DivOverlay: ƒ DivOverlay()
get FeatureGroup: ƒ FeatureGroup()
get GeoJSON: ƒ GeoJSON()
get GridLayer: ƒ GridLayer()
get ImageOverlay: ƒ ImageOverlay()
get LayerGroup: ƒ LayerGroup()
get LayersControl: ƒ LayersControl()
get LeafletConsumer: ƒ LeafletConsumer()
get LeafletProvider: ƒ LeafletProvider()
get Map: ƒ Map()
get MapComponent: ƒ MapComponent()
get MapControl: ƒ MapControl()
get MapLayer: ƒ MapLayer()
get Marker: ƒ Marker()
get Pane: ƒ Pane()
get Path: ƒ Path()
get Polygon: ƒ Polygon()
get Polyline: ƒ Polyline()
get Popup: ƒ Popup()
get Rectangle: ƒ Rectangle()
get ScaleControl: ƒ ScaleControl()
get TileLayer: ƒ TileLayer()
get Tooltip: ƒ Tooltip()
get VideoOverlay: ƒ VideoOverlay()
get WMSTileLayer: ƒ WMSTileLayer()
get ZoomControl: ƒ ZoomControl()
get withLeaflet: ƒ withLeaflet()
__proto__ :Object

jplaza21:09:44

@pesterhazy that’s what I get

pesterhazy22:09:31

Looks alright to me

pesterhazy22:09:14

Idk the npm-deps option is fraught with issues

pesterhazy22:09:38

I’d recommend using the official webpack guide or copy this demo repo: https://github.com/pesterhazy/cljs-spa-example

pesterhazy22:09:23

The doublebundle approach is guaranteed to work with all npm libs

jplaza22:09:22

The problem is that if I look into any of the keys they are undefined

jplaza22:09:46

Thanks for the link. Will give it a try again tomorrow night @pesterhazy

michael.heuberger23:09:20

hello guys … trying to find the code for the :> macro here

michael.heuberger23:09:32

it’s not in reagent’s source code

michael.heuberger23:09:51

curious here how it is implemented to allow native react stuff

justinlee23:09:00

@michael.heuberger I was confounded by that too for the longest time. It turns out that it is built into the vec-to-elem function

michael.heuberger23:09:20

how did you find this out?

justinlee23:09:51

i forget who pointed me in the right direction. i think it might have been juho