为什么多方法不能用作Reagent / Re-frame的功能?

时间:2015-10-23 10:04:56

标签: clojure clojurescript reagent re-frame

在我正在构建的小应用程序中使用Reagent和Re-frame我正在使用多方法来分派应根据应用程序状态中的值显示哪个页面:

(defmulti pages :name)

(defn main-panel []
  (let [current-route (re-frame/subscribe [:current-route])]
    (fn []
      ;...
      (pages @current-route))))

然后我有以下方法:

(defmethod layout/pages :register [_] [register-page])

register-page函数将生成实际视图:

(defn register-page []
  (let [registration-form (re-frame/subscribe [:registration-form])]
    (fn []
      [:div
       [:h1 "Register"]
       ;...
       ])))

我尝试changing my app so that the methods generated the pages directly,如:

(defmethod layout/pages :register [_]
  (let [registration-form (re-frame/subscribe [:registration-form])]
    (fn []
      [:div
       [:h1 "Register"]
       ;...
       ])))

并且没有导致任何页面被渲染。在我的主要面板中changed the call to pages to square brackets,以便Reagent能够看到它:

(defn main-panel []
  (let [current-route (re-frame/subscribe [:current-route])]
    (fn []
      ;...
      [pages @current-route])))

并且导致第一个访问过的页面工作,但之后,单击链接(导致当前路由更改)无效。

在首先加载的文件中需要定义各个方法的所有命名空间,包含init函数,以及我可以选择任何单个页面并显示它的事实证明代码正在加载(然后切换到另一页不起作用):

https://github.com/carouselapps/ninjatools/blob/master/src/cljs/ninjatools/core.cljs#L8-L12

为了调试正在进行的操作,我定义了两个路由:about:about2,一个作为函数,一个作为方法:

(defn about-page []
  (fn []
    [:div "This is the About Page."]))

(defmethod layout/pages :about [_]
  [about-page])

(defmethod layout/pages :about2 [_]
  (fn []
    [:div "This is the About 2 Page."]))

并使布局打印调用pages的结果(当然必须使用显式调用而不是方括号)。包装的函数,即工作的函数,返回:

[#object[ninjatools$pages$about_page "function ninjatools$pages$about_page(){
return (function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About Page."], null);
});
}"]]

当方法返回时:

#object[Function "function (){
return new cljs.core.PersistentVector(null, 2, 5, cljs.core.PersistentVector.EMPTY_NODE, [new cljs.core.Keyword(null,"div","div",1057191632),"This is the About 2 Page."], null);
}"]

如果我将方法更改为:

(defmethod layout/pages :about2 [_]
  [(fn []
     [:div "This is the About 2 Page."])])

即,在向量中返回函数,然后,它开始工作。如果我对包装函数进行反向更改,它将以与方法相同的方式开始失败:

(defn about-page []
  (fn []
    [:div "This is the About Page."]))

(defmethod layout/pages :about [_]
  about-page)

有一点意义,因为Reagent的语法是[function]但它应该自动调用该函数。

我也开始向浏览器输出@current-route,如:

[:main.container
 [alerts/view]
 [pages @current-route]
 [:div (pr-str @current-route)]]

我验证@current-route已正确修改并且输出已更新,而不是[pages @current-route]

我的应用的完整源代码可以在这里找到:https://github.com/carouselapps/ninjatools/tree/multi-methods

Update: corrected the arity of the methods following Michał Marczyk's answer.

4 个答案:

答案 0 :(得分:13)

所以,像这样的组件:[pages @some-ratom]
会在pages更改或@some-ratom更改时重新呈现。

从试剂的角度来看,pages自上次以来没有变化,它仍然是以前的多方法。但@some-ratom可能会发生变化,因此可能会触发重新渲染。

当此重新渲染发生时,将使用pages的缓存版本完成。毕竟,pages已发生变化的试剂似乎没有。它仍然是以前的多方法。

pages的缓存版本当然是pages的第一个版本,它是第一个版本的mutlimethod和不是新版本我们期待看到二手。

Reagent执行此缓存,因为它必须处理Form-2函数。它必须保持返回的渲染功能。

底线:由于缓存,多方法不能很好地工作,除非你找到一种方法来彻底炸毁组件并重新开始,这是当前最高投票方法的作用:
   ^{:key @current-route} [pages @current-route]
当然,炸毁组件并重新开始可能会产生不受欢迎的影响(取决于该组件中的本地状态)。

含糊不清的相关背景:
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

答案 1 :(得分:4)

我没有掌握所有细节,但显然,当我渲染这样的网页时:

[:main.container
 [alerts/view]
 [pages @current-route]]

Reagent没有注意到pages取决于@current-route的值。 Chrome React plugin让我明白了。我尝试使用ratom而不是订阅,这似乎工作正常。谢天谢地,telling Reagent/React the key to an element is easy enough

[:main.container
 [alerts/view]
 ^{:key @current-route} [pages @current-route]]

这很好用。

答案 2 :(得分:2)

跳出来的第一个问题是你的方法没有参数:

(defmethod layout/pages :register [] [register-page])
                                  ^ arglist

这里有一个空的arglist,但可能你会用一个或两个参数调用这个多方法(因为它的dispatch函数是一个关键字,关键字可以用一个或两个参数调用)。

如果你想用一个参数调用这个多方法,只需在:register方法的主体内忽略它,将上面的内容改为

(defmethod layout/pages :register [_] [register-page])
                                   ^ argument to be ignored

此外,我希望您可能想要像以前一样自己致电pages(也就是说,将更改恢复为您在问题中提到的方括号)。

这可能会也可能不会修复应用程序 - 可能还有其他问题 - 但它应该让你开始。 (如果传入任何参数,多方法肯定不适用于那些空的arglists。)

答案 3 :(得分:0)

如果你有一个包装{{1}}函数,这是一个可以被试剂缓存的常规函数​​,那该怎么办?它看起来像这样:

{{1}}