Fork me on GitHub
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 ?


@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


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


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


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


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


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


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 ?


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)


"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 ?


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


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

Ben Halton12:09:22

yeah I have - not called


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


(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


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


- 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


that sounds like a red herring to me


can you turn it into a defn?


and see if that works

Ben Halton12:09:10

you mean turn the multimethod into a defn and call ?


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


that's interesting


might be a bug in reagent


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


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


e.g. implements IFn


maybe that check doesn't work properly for defmultis


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


(defn foo* [] (foo))


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


I look forward to it!

Ben Halton13:09:02

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


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]


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).


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


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


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...


@gingerwizard that's a crazy issue


still haven't fully understood why this fails


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 ?


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


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


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


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


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


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