在一个使用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
函数将生成实际 View 的位置:(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函数)中,所有定义单个方法的 namespace 都是必需的,而且我可以选择任何一个页面并显示它的事实证明代码正在加载(然后,切换到另一页不会不起作用):
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."])])
也就是说,以 vector 返回函数,然后它开始工作。而且,如果我对包装函数进行了反向更改,则它以与方法相同的方式开始失败:
(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.
最佳答案
因此,像这样的组件:[pages @some-ratom]
将在pages
更改或@some-ratom
更改时重新呈现。
从试剂的 Angular 来看,pages
自上次以来就没有改变过,它仍然是以前的相同多方法。但是@some-ratom
可能会更改,因此可能触发重新提交。
但是发生在此重新渲染时,将使用pages
的缓存版本来完成。毕竟,对于pages
而言,似乎没有改变。它仍然是以前的相同方法。
当然,pages
的缓存版本将是呈现的pages
的第一个版本-mutlimethod和的第一个版本,而不是,我们希望看到使用的新版本。
试剂执行此缓存,因为它必须处理Form-2函数。它必须保留返回的render函数。
最重要的是:由于存在缓存,除非您找到一种方法来完全炸毁该组件并重新开始,否则多重方法将无法很好地工作,这是目前最受好评的方法:
^{: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
关于clojure - 为什么多方法不能用作试剂/重组的功能?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33299746/