Why are multi-methods not working as functions for Reagent/Re-frame?

前端 未结 4 1145
粉色の甜心
粉色の甜心 2021-02-12 12:53

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         


        
相关标签:
4条回答
  • 2021-02-12 13:27

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

    0 讨论(0)
  • 2021-02-12 13:31

    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

    0 讨论(0)
  • 2021-02-12 13:34

    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))
    
    0 讨论(0)
  • 2021-02-12 13:39

    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.

    0 讨论(0)
提交回复
热议问题