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

pup*_*eno 19 clojure clojurescript reagent re-frame

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

(defmulti pages :name)

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

然后我有方法,如:

(defmethod layout/pages :register [_] [register-page])
Run Code Online (Sandbox Code Playgroud)

register-page功能会产生实际的观点:

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

我尝试更改我的应用程序,以便方法直接生成页面,如:

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

这导致没有任何页面被渲染.在我的主面板中,我将调用更改pages为方括号,以便Reagent可以看到它:

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

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

在首先加载的文件中需要定义各个方法的所有命名空间,其中包含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."]))
Run Code Online (Sandbox Code Playgroud)

并使布局打印调用的结果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);
});
}"]]
Run Code Online (Sandbox Code Playgroud)

而方法返回:

#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);
}"]
Run Code Online (Sandbox Code Playgroud)

如果我将方法更改为:

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

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

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

(defmethod layout/pages :about [_]
  about-page)
Run Code Online (Sandbox Code Playgroud)

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

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

[:main.container
 [alerts/view]
 [pages @current-route]
 [:div (pr-str @current-route)]]
Run Code Online (Sandbox Code Playgroud)

我验证@current-route正在被正确修改,输出更新,只是没有[pages @current-route].

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

更新:根据MichałMarczyk的回答更正了方法的优点.

Mik*_*son 14

因此,这样的组件:[pages @some-ratom]
pages更改或@some-ratom更改时将重新呈现.

从试剂的角度来看,pages自上次以来没有变化,它仍然是以前的多方法.但@some-ratom可能会改变,因此可能引发重新报复.

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

pages当然,缓存版本的will将是第一个版本的版本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 /维基/当-DO组件更新%3F


pup*_*eno 5

我没有所有的细节,但是很显然,当我渲染这样的页面时:

[:main.container
 [alerts/view]
 [pages @current-route]]
Run Code Online (Sandbox Code Playgroud)

试剂未能注意到pages取决于的值@current-route。该浏览器插件的反应让我看着办吧。我试着用一种代替订阅的形式,这似乎很好用。幸运的是,告诉Reagent / React元素的密钥很容易

[:main.container
 [alerts/view]
 ^{:key @current-route} [pages @current-route]]
Run Code Online (Sandbox Code Playgroud)

那很好。