Fork me on GitHub
#reagent
<
2019-01-24
>
reefersleep06:01:19

Is there a pattern for doing something upon mounting of a component, and then again each time that component receives arguments that were different from the last time? What I want is for a page to load its data from the backend the first time the user visits it, and then subsequently each time they stay on the page (no remounting), but change the arguments (id of what they want to look at).

reefersleep06:01:57

I thought I could use a lifecycle hook with reagent/create-class, I’ve tried with

:component-will-update
(fn [component [_ & [{:keys [entity-id] :as routeparams} :as new-args]]]
  (let [old-args (reagent/children component)]
    (when (not= new-args old-args)
    (prn "reloading page state!")
    (populate-page-state entity-id))))})
, but it doesn’t feel solid.

reefersleep06:01:00

Hm, maybe it is though. It seems to work for reloading upon new args, and then I just have to set up initial load with the form 2 pattern.

reefersleep06:01:14

So, something like this

reefersleep06:01:13

:reagent-render 
(fn [{:keys [entity-id] :as routeparams}]
  (populate-page-state entity-id) ;; load on first visit
  (fn [{:keys [entity-id] :as routeparams}]
    [actual-component … ]))
:component-will-update
(fn [component [_ & [{:keys [entity-id] :as routeparams} :as new-args]]]
  (let [old-args (reagent/children component)]
    (when (not= new-args old-args)
    (prn "reloading page state!")
    (populate-page-state entity-id))))}) ;; load on same-page argument changes

orestis07:01:55

I think component-will-update is deprecated - replaced with component-did-update

orestis07:01:58

I think you’re approaching this from the wrong end though. Why not fetch the data from the place the user is selecting what they want to do (presumably a function you already wrote)

lilactown08:01:45

orestis is right. In raw React (and esp. with the new Hooks API), your approach might be OK. but with reagent, it will be difficult

lilactown08:01:45

@reefersleep I would store the state of the user’s data in an atom somewhere and update that on each event that would change the props

reefersleep09:01:16

@orestis @lilactown cheers for the feedback 🙂 I think I’m a fan of more explicit state updates at the site of user interaction, as well.

jeremy17:01:18

I’ve been trying the following

[:> js/reactJsonView {:src ""
                                    :indentWidth 2
                                    :collapsed @collapsed}] 
but it’s just complaining Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object.

jeremy17:01:35

I’m not sure how to debug it.

thheller17:01:42

@jeremy642 from those generated externs I guess it should be js/reactJsonView.default

jeremy17:01:57

@thheller Thanks. When I try that i get React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it’s defined in, or you might have mixed up default and named imports.

thheller17:01:58

did you try looking at the reactJsonView object in the browser console?

thheller17:01:25

check if it has a .default property? (CLJS may be incorrectly munging default to default$)

jeremy17:01:34

You mean in devtools? Console doesn’t say anything specifically for reactJsonView.

thheller17:01:13

just type reactJsonView in the console and look at the object

thheller17:01:47

or (js/console.dir js/reactJsonView) in the CLJS code somewhere

jeremy17:01:48

I don’t think the react app is even loading because of the error.

jeremy18:01:46

I get what you’re saying.

jeremy18:01:49

Sorry slow

jeremy18:01:23

Looks like default is a function f t(e)

thheller19:01:19

@jeremy642 try [:> (goog.object/get js/reactJsonView "default") ...]

WhoNeedszZz22:01:20

Or you can always use the built-in aget instead of yet another dependency

thheller22:01:19

goog.object is always included in any cljs build so no extra code added

thheller22:01:27

cljs.core uses it

thheller22:01:33

aget is for arrays

WhoNeedszZz22:01:39

Didn't know that, thanks. Still would save an extra require line if you care.

WhoNeedszZz23:01:13

So I see what you're saying now, but then can you explain this?

:primary {:main (aget (.-teal mui-colors) 500)
                :dark "#1976d2"}

WhoNeedszZz23:01:31

That works just fine, but the examples for how to use "@material-ui/core/colors" is to use goog.object/get. So what's actually going on here?

thheller23:01:36

aget is meant for arrays. it just also works on objects given how javascript works

WhoNeedszZz23:01:15

Ah, ok. Makes sense

thheller19:01:51

or set :language-out :ecmascript5 in your build config

jeremy19:01:05

Yeah that worked.

jeremy19:01:12

Could you explain what that’s doing?

thheller19:01:53

default used to be a reserved name in JS so the compiler used to rename it to default$ to avoid using the reserved name

thheller19:01:27

it is no longer reserved since ES5 so perfectly valid to use it

jeremy19:01:44

Oh okay. I’ll try to keep that in mind.

thheller19:01:22

I fixed that issue in shadow-cljs. not sure there is a ticket for CLJS yet

jeremy19:01:04

Building with lein and I’m not sure. But I appreciate the help on that.

thheller19:01:30

:language-out should take care of it

thheller19:01:43

so js/reactJsonView.default should work without goog.object

thheller19:01:49

but goog.object is guaranteed to work

jeremy19:01:34

I put it in my notes. I’ll try out the language-out in a bit.

WhoNeedszZz22:01:57

How do you guys handle the fact that modern JS features don't work on IE/Edge and you made this fancy React (reagent) site, but it doesn't display correctly at all on those awful browsers that there are people out there crazy enough to use them?

lilactown23:01:23

Write code with compat in mind 😜

lilactown23:01:33

Server render more than client render