Fork me on GitHub
#reagent
<
2016-09-21
>
Ben Halton10:09:48

Hi I am new to reagent / clojurescript so apologies if this is a dim question

Ben Halton10:09:24

It works basically by having a function 'home' that listens to the session for changes to the route:

Ben Halton10:09:35

(defn- home [state]
  (let [route (session/get :route)
        page (:current-page route)]
    (page-contents page)))

Ben Halton10:09:48

and renders the page calling the multimethod page-contents

Ben Halton10:09:01

So I have a component like

Ben Halton10:09:08

(defmethod page-contents :foo []
	[:a {:href (bidi/path-for routes :bar)} "link"])

Ben Halton10:09:18

and it works fine, but if I have the same component but returned as a function:

Ben Halton10:09:28

(defmethod page-contents :foo []
	(fn [] 
		[:a {:href (bidi/path-for routes :bar)} "link"]))

Ben Halton10:09:40

Then when I click on the link the url in the browser changes but the home function doesn't register the change to the session and re-render the page

Ben Halton10:09:53

I can see that accountant / bidi are working as expected and the session is being updated.

Ben Halton10:09:01

but the home function doesn't seem to 'notice'

Ben Halton10:09:12

any thoughts on what the difference is here ?

pez11:09:04

@gingerwizard: Strange indeed. In the project I am working with almost all the page components return a function. Do you get any errors/warnings in the console?

Ben Halton11:09:31

@pez well yes this is the thing - most of my components will need to be like that

Ben Halton11:09:10

no errors in console and I can see accountant/bidi setting the session state

Ben Halton11:09:34

and the url is changing as expected

Ben Halton11:09:38

it seems so fundamental that I must be missing something

pez11:09:18

I think it might be an error in the example that gets visible when you return the component as a function...

pez11:09:45

@gingerwizard: What if you include the doc string? (Before the lambda)

pez11:09:04

Forget that with the doc string… Now I can reproduce the problem in the example app. When I click once more on the link I get a full app reload.

Ben Halton11:09:59

@pez I don't see the app reload behaviour

pesterhazy12:09:56

add more logging everywhere; my guess is that there's a mistake or conceptual misunderstanding somewhere

pesterhazy12:09:05

I don't think defmethod vs defn should make a difference

pesterhazy12:09:41

also (defn x [] [:div]) should be exactly equivalent to (defn x [] (fn [] [:div]))

Ben Halton12:09:16

@pesterhazy I have added logging up to the point that the 'magic' happens

Ben Halton12:09:37

in my repl I can see the state of the session is what I would expect

Ben Halton12:09:59

not sure how to debug the interaction of the session and the reagent component ?

pesterhazy12:09:36

can you say in 1 sentence what you expect to see and what you actually see?

Ben Halton12:09:10

so I expect to see: accountant gets a bidi match on the url and pushes the route into the session

Ben Halton12:09:20

(this happens as expected)

pesterhazy12:09:43

"session" is an "accountant" concept?

Ben Halton12:09:01

a reagent session

Ben Halton12:09:25

i.e. as I understand it just a ratom ?

pesterhazy12:09:29

you meant the global state atom?

Ben Halton12:09:20

then I expect the function page which derefs the session to "see" that the route has changed and call the multimethod

pesterhazy12:09:11

why not add logging to the render fn that derefs the ratom?

Ben Halton12:09:22

yeah I have - not called

pesterhazy12:09:44

ok; can you try manually swap!ping the atom?

pesterhazy12:09:58

(e.g. using a button that does that)

Ben Halton12:09:11

yes I have and that doesn't work (done via cljs repl)

Ben Halton12:09:03

it's like the component has stopped listening

pesterhazy12:09:05

so it sounds like the issue is simply that you have a component that derefs a ratom and is visible but doesn't get rerendered when you swap the ratom, correct?

Ben Halton12:09:18

yes I think so

pesterhazy12:09:53

- check that it's actually a ratom (not clojure.core/atom)

Ben Halton12:09:58

but as I say - works when I return [:a {:href... but not (fn[] [:a {:href

Ben Halton12:09:12

so the only difference is that

pesterhazy12:09:28

that sounds like a red herring to me

pesterhazy12:09:59

can you turn it into a defn?

pesterhazy12:09:10

and see if that works

Ben Halton12:09:10

you mean turn the multimethod into a defn and call ?

pesterhazy12:09:43

defmulti+defmethod is equivalent to a defn with a cond

Ben Halton12:09:13

sure - just checking what you mean -

Ben Halton12:09:55

hmm ok that looks like it has made a difference

Ben Halton12:09:50

so I replaced (defmulti page-content :foo [] (fn[] [:a ...

Ben Halton12:09:15

with (defn foo[] (fn [] [:a ...

Ben Halton12:09:42

and called that - and I see that the component that derefs the session spots the change

pesterhazy12:09:36

that's interesting

pesterhazy12:09:39

might be a bug in reagent

pesterhazy12:09:02

are you on a recent version of reagent?

Ben Halton12:09:30

yes 0.6.0-alpha

Ben Halton12:09:04

I'm beginning to confuse myself a bit - gonna have a cup of tea and come back and see if I am doing something really stupid

pesterhazy12:09:15

it could be that reagent checks if the fn meets certain conditions

pesterhazy12:09:21

e.g. implements IFn

pesterhazy12:09:38

maybe that check doesn't work properly for defmultis

pesterhazy12:09:56

might be worth wrapping the defmulti in a defn to see if that works

pesterhazy12:09:23

(defn foo* [] (foo))

pesterhazy12:09:38

might also be a new issue with 0.6.0 for all we know

Ben Halton12:09:26

OK - so I might have a step forward

Ben Halton12:09:37

if I define my component like this

Ben Halton12:09:46

(defn home []
  (fn []
    (let [page (:current-page (session/get :route))]
      [page-contents page])))

Ben Halton12:09:27

it does pick up the change in the session (the rendering doesn't seem to work but maybe that's a different problem)

Ben Halton12:09:43

based on the example I was copying I was using this

Ben Halton12:09:57

(defn home []
  (fn []
    (let [page (:current-page (session/get :route))]
      (page-contents page))))

Ben Halton12:09:28

so with logging in that function I can see the route change in the session and home getting called

Ben Halton12:09:51

it doesn't actually render the new route though so that's another issue

Ben Halton13:09:19

OK have run @pez example that I copied in the first place and it exhibits the same behaviour

Ben Halton13:09:50

OK I have it

Ben Halton13:09:07

it is multimethod madness

Ben Halton13:09:30

2 changes to get it all working I think @pez

pez13:09:56

I look forward to it!

Ben Halton13:09:02

1 use [] not () for calling page-contents

pez13:09:17

I tried that too. =)

Ben Halton13:09:38

2/ ^{:key page}

Ben Halton13:09:59

so in your page func

Ben Halton13:09:12

^{:key page} [page-contents page]

pez13:09:40

React key?

Ben Halton13:09:41

the stackoverflow question explains all I think

Ben Halton13:09:09

Bottom line: because of the caching, multimethods won't work very well, unless you find a way to completely blow up the component and start again, which is what the currently-top-voted approach does: ^{:key @current-route} [pages @current-route] Of course, blowing up the component and starting again might have its own unwelcome implications (depending on what local state is held in that component).

pez13:09:31

I wonder what makes it work in the project I work on. (I mainly dab with svg things and my colleagues are the clojure experts.)

Ben Halton13:09:41

yeah now that is strange

pez13:09:50

We don't use session there for this, but instead datascript and posh and stuff.

pez13:09:04

Since posh is involved maybe there are more things that can trigger the reaction. In any case now I am a bit worried this caching thing is luring in the background ready to bite us. 😄

Ben Halton13:09:28

well thanks for your help @pez and @pesterhazy better get on with some real work now...

pesterhazy14:09:03

@gingerwizard that's a crazy issue

pesterhazy14:09:30

still haven't fully understood why this fails

jvuillermet20:09:56

Hi, I'm struggling with a controlled input that parse its content: the user is able to enter a list of tag separated by commas "stuff,tag,hello" I would like to use the onChange handler to update my state with the parsed content (split input-value ",") so I get a vector on my state. The problem is that I can't synchronize properly with the :value of the input because if I type "hello," the comma wont appear because it's splited then joined again How do you handle this ?

jvuillermet20:09:03

Using defaultValue helps but then I have other issues. Storing the raw input value in the state would work but feel wrong as other parts of the app need this state and should have a vector directly

jjfine20:09:13

you could store both the raw input value and the parsed value

jjfine20:09:36

only the input itself would need to access the raw value, everything else could use the parsed one

jjfine20:09:53

didn't try this out so i could be way wrong

jjfine20:09:11

there'd be a problem if the parsed value could be modified in another place...

jvuillermet20:09:25

That's the only thing I didnt try. Should work in my case