In a small app I\'m building that uses Reagent and Re-frame I\'m using multi-methods to dispatch which page should be shown based on a value in the app state:
(d
The first problem that jumps out at me is that your methods take no arguments:
(defmethod layout/pages :register [] [register-page])
^ arglist
Here you have an empty arglist, but presumably you'll be calling this multimethod with one or two arguments (since its dispatch function is a keyword and keywords can be called with one or two arguments).
If you want to call this multimethod with a single argument and just ignore it inside the body of the :register
method, change the above to
(defmethod layout/pages :register [_] [register-page])
^ argument to be ignored
Also, I expect you'll probably want to call pages
yourself like you previously did (that is, revert the change to square brackets that you mentioned in the question).
This may or may not fix the app – there may be other problems – but it should get you started. (The multimethod will definitely not work with those empty arglists if you pass in any arguments.)
So, a component like this: [pages @some-ratom]
will rerender when pages
changes or @some-ratom
changes.
From reagent's point of view, pages
hasn't changed since last time, it is still the same multi-method it was before. But @some-ratom
might change, so that could trigger a rerender.
But when this rerender happens it will be done using a cached version of pages
. After all, it does not appear to reagent that pages
has changed. It is still the same multimethod it was before.
The cached version of pages
will, of course, be the first version of pages
which was rendered - the first version of the mutlimethod and not the new version we expect to see used.
Reagent does this caching because it must handle Form-2 functions. It has to keep the returned render function.
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).
Vaguely Related Background:
https://github.com/Day8/re-frame/wiki/Creating-Reagent-Components#appendix-a---lifting-the-lid-slightly
https://github.com/Day8/re-frame/wiki/When-do-components-update%3F
How about if you instead have a wrapper pages-component
function which is a regular function that can be cached by reagent. It would look like this:
(defn pages-component [state]
(layout/pages @state))
I don't have all the details, but apparently, when I was rendering pages like this:
[:main.container
[alerts/view]
[pages @current-route]]
Reagent was failing to notice that pages
depended on the value of @current-route
. The Chrome React plugin helped me figure it out. I tried using a ratom instead of a subscription and that seemed to work fine. Thankfully, telling Reagent/React the key to an element is easy enough:
[:main.container
[alerts/view]
^{:key @current-route} [pages @current-route]]
That works just fine.